Merge MOV to MP4 Script

Prompt:

ubuntu 24.04: shell script to merge all .mov files into a directory and convert into a single .mp4 file

Response:

You’ll need to use the command-line tool FFmpeg for this task. It’s the standard utility on Ubuntu for video processing.

If you don’t have FFmpeg installed on your Ubuntu 24.04 system, first run:

sudo apt update
sudo apt install ffmpeg

Shell Script to Merge and Convert .mov Files

This script uses the concat demuxer feature of FFmpeg, which is the most efficient way to combine videos that share the same codecs and other properties (like resolution and framerate). Since many .mov files from the same source (like a phone or camera) often share these properties, using -c copy avoids re-encoding, making the process extremely fast and lossless.

merge_mov_to_mp4.sh

#!/bin/bash

# --- Configuration ---
OUTPUT_FILE="merged_video.mp4"
LIST_FILE="mov_file_list.txt"
# ---------------------

echo "Starting MOV file merge and conversion..."
echo "-----------------------------------------"

# 1. Create a temporary file list
# This finds all .mov files (case-insensitive) in the current directory and formats them
# for FFmpeg's concat demuxer (e.g., 'file 'filename.mov'').
# The 'sort' ensures a predictable order (e.g., IMG_0001.MOV, IMG_0002.MOV).
find . -maxdepth 1 -iname '*.mov' -printf "file '%p'\n" | sort > $LIST_FILE

# Check if any files were found
if [ ! -s "$LIST_FILE" ]; then
    echo "❌ Error: No .mov files found in the current directory. Exiting."
    rm -f "$LIST_FILE"
    exit 1
fi

echo "Found the following files to merge (order is important):"
cat "$LIST_FILE"
echo "-----------------------------------------"

# 2. Concatenate and convert using FFmpeg
# -f concat: Use the concat demuxer
# -safe 0: Allows relative or complex file paths in the list file
# -i $LIST_FILE: Specifies the input list file
# -c copy: Copies the existing streams without re-encoding (FAST and LOSSLESS)
# -movflags +faststart: Optimizes the MP4 file for web streaming
ffmpeg -f concat -safe 0 -i "$LIST_FILE" -c copy -movflags +faststart "$OUTPUT_FILE"

# Check the exit status of FFmpeg
if [ $? -eq 0 ]; then
    echo "-----------------------------------------"
    echo "✅ Success! All .mov files merged into $OUTPUT_FILE"
else
    echo "-----------------------------------------"
    echo "⚠️ FFmpeg command failed. This often happens if the input .mov files"
    echo "   have different video/audio codecs or stream properties. Trying re-encode..."
    
    # 3. Fallback: Re-encode if the -c copy (fast) method fails
    # This ensures compatibility but is much slower.
    # Recommended settings for MP4 (H.264 video, AAC audio):
    ffmpeg -f concat -safe 0 -i "$LIST_FILE" -c:v libx264 -preset medium -crf 23 -c:a aac -b:a 128k -movflags +faststart "re_encoded_$OUTPUT_FILE"
    
    if [ $? -eq 0 ]; then
        echo "-----------------------------------------"
        echo "✅ Success! Files merged into re_encoded_$OUTPUT_FILE (Re-encoded)."
    else
        echo "-----------------------------------------"
        echo "❌ Critical Error: Both fast copy and re-encode methods failed. Check FFmpeg output for details."
    fi
fi

# 4. Clean up the temporary file
rm -f "$LIST_FILE"

echo "-----------------------------------------"
echo "Script finished."

How to Use the Script

  1. Save the script: Save the code above into a file named merge_mov_to_mp4.sh.
  2. Make it executable: Give the script permission to run:
    chmod +x merge_mov_to_mp4.sh

  3. Place and Run:
    • Move or copy all the .mov files you want to merge into a single directory.
    • Navigate to that directory in your terminal: cd /path/to/your/videos
    • Execute the script:
      ./merge_mov_to_mp4.sh

Important Notes

  • Order: The files will be merged in alphabetical order by their filename (due to the sort command). You should name your files sequentially (e.g., clip_01.mov, clip_02.mov) if the order is critical.
  • Codec Compatibility: The script first attempts a fast, lossless copy (-c copy). If this fails (usually because the individual .mov files have different video or audio settings), it automatically falls back to a slower re-encode method using common, compatible MP4 codecs (H.264/AAC), saving the result as re_encoded_merged_video.mp4.
  • Case-Insensitive: The find command uses -iname '*.mov' to find files with extensions like .mov or .MOV.

Prompt:

the resulting file is really big, add a compression

Response:

A larger file size from the merge usually happens when the fallback re-encode option is used, as it processes the video streams. You can add compression to the re-encoding step by adjusting the Constant Rate Factor (CRF) value in the FFmpeg command.

Here is the modified script with an updated re-encode command to target a smaller file size while maintaining good quality.


Modified Shell Script with Compression

This script’s first attempt is still the fast, lossless stream copy (-c copy). If that fails, the fallback re-encode step will use a lower CRF value for a smaller file.

merge_mov_to_mp4_compressed.sh

#!/bin/bash

# --- Configuration ---
OUTPUT_FILE="merged_video.mp4"
LIST_FILE="mov_file_list.txt"
# Recommended CRF is 18-28. Lower is higher quality (bigger file).
# CRF 23 is the default. Setting it higher (e.g., 28) compresses more, resulting in a smaller file.
# Try 26 for a good balance of size and quality.
CRF_VALUE=26
# ---------------------

echo "Starting MOV file merge and conversion..."
echo "-----------------------------------------"

# 1. Create a temporary file list
find . -maxdepth 1 -iname '*.mov' -printf "file '%p'\n" | sort > $LIST_FILE

# Check if any files were found
if [ ! -s "$LIST_FILE" ]; then
    echo "❌ Error: No .mov files found in the current directory. Exiting."
    rm -f "$LIST_FILE"
    exit 1
fi

echo "Found the following files to merge (order is important):"
cat "$LIST_FILE"
echo "-----------------------------------------"

# 2. Concatenate and convert using FFmpeg (Fast, Lossless Attempt)
echo "Attempting fast, lossless stream copy (-c copy)..."
ffmpeg -f concat -safe 0 -i "$LIST_FILE" -c copy -movflags +faststart "$OUTPUT_FILE"

# Check the exit status of FFmpeg
if [ $? -eq 0 ]; then
    echo "-----------------------------------------"
    echo "✅ Success! All .mov files merged into $OUTPUT_FILE (Lossless copy)."
else
    echo "-----------------------------------------"
    echo "⚠️ Stream copy failed (mismatched video/audio properties)."
    echo "   Falling back to slower re-encode with compression (CRF $CRF_VALUE)..."
    
    # 3. Fallback: Re-encode with compression
    # -c:v libx264: Use the H.264 video encoder (standard for MP4).
    # -preset medium: Controls the encoding speed/efficiency (medium is a good balance).
    # -crf $CRF_VALUE: Constant Rate Factor (CRF). Higher number = smaller file/lower quality.
    # -c:a aac -b:a 128k: Sets the audio codec to AAC with a standard bitrate.
    RE_ENCODED_OUTPUT="re_encoded_compressed_$OUTPUT_FILE"
    ffmpeg -f concat -safe 0 -i "$LIST_FILE" \
           -c:v libx264 -preset medium -crf $CRF_VALUE \
           -c:a aac -b:a 128k -movflags +faststart "$RE_ENCODED_OUTPUT"
    
    if [ $? -eq 0 ]; then
        echo "-----------------------------------------"
        echo "✅ Success! Files merged into $RE_ENCODED_OUTPUT (Compressed re-encode)."
    else
        echo "-----------------------------------------"
        echo "❌ Critical Error: Both methods failed. Check FFmpeg output for details."
    fi
fi

# 4. Clean up the temporary file
rm -f "$LIST_FILE"

echo "-----------------------------------------"
echo "Script finished."

How Compression Works (CRF)

The key to file size reduction in the re-encode step is the Constant Rate Factor ( $CRF$ ).

  • FFmpeg Command: The flag used is -crf $CRF_VALUE.
  • CRF Range: It typically ranges from 0 to 51.
  • Quality vs. Size:
    • Lower CRF (e.g., 18): Means higher quality and a larger file size (visually near-lossless).
    • Higher CRF (e.g., 28): Means lower quality and a smaller file size (more compression).
  • Default: The default value for $H.264$ encoding is 23. By increasing it to 26 (as in the script), you tell FFmpeg to use more compression, resulting in a noticeably smaller file with a very acceptable quality drop. Feel free to adjust the CRF_VALUE variable to fine-tune the size/quality trade-off.

Prompt:

always reencode

Response:

This version of the script removes the lossless copy attempt and proceeds directly to re-encoding and merging with compression, ensuring a consistent and compressed MP4 output file every time.

always_reencode_mov.sh

#!/bin/bash

# --- Configuration ---
OUTPUT_FILE="merged_and_compressed.mp4"
LIST_FILE="mov_file_list.txt"
# CRF (Constant Rate Factor) controls the file size and quality.
# Recommended range is 18-28. Higher CRF means smaller file/more compression.
# 26 provides a good balance between size reduction and visual quality.
CRF_VALUE=26
# ---------------------

echo "Starting MOV file merge and conversion (Always Re-encoding)..."
echo "Target CRF for compression: $CRF_VALUE"
echo "-----------------------------------------"

# 1. Create a temporary file list
# Finds all .mov files (case-insensitive) and sorts them alphabetically.
find . -maxdepth 1 -iname '*.mov' -printf "file '%p'\n" | sort > "$LIST_FILE"

# Check if any files were found
if [ ! -s "$LIST_FILE" ]; then
    echo "❌ Error: No .mov files found in the current directory. Exiting."
    rm -f "$LIST_FILE"
    exit 1
fi

echo "Found the following files to merge (order is important):"
cat "$LIST_FILE"
echo "-----------------------------------------"

# 2. Concatenate and re-encode using FFmpeg
# This process is slower but guarantees a single, consistent MP4 file with compression.
# -c:v libx264 -crf $CRF_VALUE: Video codec H.264 with desired compression level.
# -c:a aac -b:a 128k: Audio codec AAC with a standard bitrate.
ffmpeg -f concat -safe 0 -i "$LIST_FILE" \
       -c:v libx264 -preset medium -crf "$CRF_VALUE" \
       -c:a aac -b:a 128k -movflags +faststart "$OUTPUT_FILE"

# Check the exit status of FFmpeg
if [ $? -eq 0 ]; then
    echo "-----------------------------------------"
    echo "✅ Success! All .mov files merged and re-encoded into $OUTPUT_FILE"
else
    echo "-----------------------------------------"
    echo "❌ Critical Error: FFmpeg re-encode failed. Check output for details."
fi

# 3. Clean up the temporary file
rm -f "$LIST_FILE"

echo "-----------------------------------------"
echo "Script finished."

How to Use and Adjust Compression

  1. Save: Save the code above into a file named always_reencode_mov.sh.
  2. Make Executable: Run chmod +x always_reencode_mov.sh.
  3. Run: Place your .mov files in a directory, navigate there, and run ./always_reencode_mov.sh.

If the resulting file is still too big, you can increase the compression further by changing the CRF_VALUE at the top of the script (e.g., from 26 to 28). If the quality is too low, decrease the value (e.g., to 24).