Skip to content

Instantly share code, notes, and snippets.

@crookm
Created April 15, 2026 09:13
Show Gist options
  • Select an option

  • Save crookm/6c3e9a3d328cb9b1b1aeca85dd4bfdc3 to your computer and use it in GitHub Desktop.

Select an option

Save crookm/6c3e9a3d328cb9b1b1aeca85dd4bfdc3 to your computer and use it in GitHub Desktop.
A simple bash script to extract video sections between chapters using ffmpeg
#!/bin/bash
# Script to extract video section between chapters using ffmpeg
# Usage: ./extract_chapters.sh <input_file> <start_chapter_number> <end_chapter_number>
set -e # Exit on any error
# Function to display usage
usage() {
echo "Usage: $0 <input_file> <start_chapter_number> <end_chapter_number>"
echo "Example: $0 movie.mkv 3 6"
echo "This will extract from chapter 3 to chapter 6 (inclusive)"
echo "Note: Chapter numbers start from 1 (first chapter is chapter 1)"
exit 1
}
# Check if correct number of arguments provided
if [ $# -ne 3 ]; then
echo "Error: Incorrect number of arguments"
usage
fi
INPUT_FILE="$1"
START_CHAPTER="$2"
END_CHAPTER="$3"
# Validate input file exists
if [ ! -f "$INPUT_FILE" ]; then
echo "Error: Input file '$INPUT_FILE' does not exist"
exit 1
fi
# Validate chapter numbers are positive integers
if ! [[ "$START_CHAPTER" =~ ^[0-9]+$ ]] || ! [[ "$END_CHAPTER" =~ ^[0-9]+$ ]]; then
echo "Error: Chapter numbers must be positive integers"
exit 1
fi
# Validate chapter numbers are at least 1
if [ "$START_CHAPTER" -lt 1 ] || [ "$END_CHAPTER" -lt 1 ]; then
echo "Error: Chapter numbers must be 1 or greater (chapters start from 1)"
exit 1
fi
# Validate start chapter is not greater than end chapter
if [ "$START_CHAPTER" -gt "$END_CHAPTER" ]; then
echo "Error: Start chapter number cannot be greater than end chapter number"
exit 1
fi
echo "Extracting chapters $START_CHAPTER to $END_CHAPTER from '$INPUT_FILE'"
# Get chapter information from the video file
echo "Reading chapter information..."
CHAPTER_INFO=$(ffprobe -v quiet -print_format json -show_chapters "$INPUT_FILE" 2>/dev/null)
if [ -z "$CHAPTER_INFO" ] || [ "$CHAPTER_INFO" = '{"chapters":[]}' ]; then
echo "Error: No chapters found in the input file"
exit 1
fi
# Extract chapter count
CHAPTER_COUNT=$(echo "$CHAPTER_INFO" | jq '.chapters | length')
echo "Found $CHAPTER_COUNT chapters in the file"
# Convert user's 1-based chapter numbers to 0-based indexes for ffmpeg
START_INDEX=$((START_CHAPTER - 1))
END_INDEX=$((END_CHAPTER - 1))
# Validate chapter numbers are within range
if [ "$START_INDEX" -ge "$CHAPTER_COUNT" ] || [ "$END_INDEX" -ge "$CHAPTER_COUNT" ]; then
echo "Error: Chapter number out of range. Available chapters: 1 to $CHAPTER_COUNT"
exit 1
fi
# Get start time of the start chapter (using 0-based index)
START_TIME=$(echo "$CHAPTER_INFO" | jq -r ".chapters[$START_INDEX].start_time")
# Get end time of the end chapter (using 0-based index)
END_TIME=$(echo "$CHAPTER_INFO" | jq -r ".chapters[$END_INDEX].end_time")
echo "Start time: ${START_TIME}s"
echo "End time: ${END_TIME}s"
# Generate output filename (using original 1-based chapter numbers)
INPUT_DIR=$(dirname "$INPUT_FILE")
INPUT_BASENAME=$(basename "$INPUT_FILE")
INPUT_NAME="${INPUT_BASENAME%.*}"
INPUT_EXT="${INPUT_BASENAME##*.}"
OUTPUT_FILE="${INPUT_DIR}/${INPUT_NAME}_ch${START_CHAPTER}-${END_CHAPTER}.${INPUT_EXT}"
echo "Output file: $OUTPUT_FILE"
# Check if output file already exists
if [ -f "$OUTPUT_FILE" ]; then
read -p "Output file already exists. Overwrite? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Operation cancelled"
exit 0
fi
fi
# Run ffmpeg command to extract the section
echo "Extracting video section..."
ffmpeg -i "$INPUT_FILE" \
-ss "$START_TIME" \
-to "$END_TIME" \
-c copy \
-map 0 \
-avoid_negative_ts make_zero \
"$OUTPUT_FILE"
if [ $? -eq 0 ]; then
echo "Successfully extracted chapters $START_CHAPTER-$END_CHAPTER to: $OUTPUT_FILE"
# Display file size information
INPUT_SIZE=$(stat -c%s "$INPUT_FILE" 2>/dev/null || echo "unknown")
OUTPUT_SIZE=$(stat -c%s "$OUTPUT_FILE" 2>/dev/null || echo "unknown")
if [ "$INPUT_SIZE" != "unknown" ] && [ "$OUTPUT_SIZE" != "unknown" ]; then
INPUT_MB=$((INPUT_SIZE / 1024 / 1024))
OUTPUT_MB=$((OUTPUT_SIZE / 1024 / 1024))
echo "Original file size: ${INPUT_MB}MB"
echo "Extracted file size: ${OUTPUT_MB}MB"
fi
else
echo "Error: ffmpeg extraction failed"
exit 1
fi
@crookm

crookm commented Apr 15, 2026

Copy link
Copy Markdown
Author

FFmpeg Chapter Extractor

A simple bash script to extract video sections between chapters using ffmpeg.

What it does

Extracts a portion of a video file from one chapter to another, preserving the original quality by copying streams without re-encoding.

Requirements

  • ffmpeg - for video processing
  • ffprobe - for reading chapter information (usually comes with ffmpeg)
  • jq - for parsing JSON output from ffprobe

Usage

./ffmpeg_extract_video_chapters.sh <input_file> <start_chapter> <end_chapter>
  • input_file: Path to your video file
  • start_chapter: Chapter number to start from (starting at 1)
  • end_chapter: Chapter number to end at (inclusive)

Examples

# Extract chapters 2 through 4
./ffmpeg_extract_video_chapters.sh video.mkv 2 4

# Extract just chapter 1
./ffmpeg_extract_video_chapters.sh video.mp4 1 1

Output

The script creates a new file in the same directory as the input file, with the naming pattern:

original_filename_ch<start>-<end>.extension

For example: video_ch2-4.mkv

Notes

  • Chapter numbering starts from 1 (like in handbrake, not 0 like in actual ffmpeg)
  • The script will show you how many chapters are available if you specify invalid ranges
  • File sizes are displayed after successful extraction
  • The script will ask before overwriting existing output files

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment