Created
April 15, 2026 09:13
-
-
Save crookm/6c3e9a3d328cb9b1b1aeca85dd4bfdc3 to your computer and use it in GitHub Desktop.
A simple bash script to extract video sections between chapters using ffmpeg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 processingffprobe- for reading chapter information (usually comes with ffmpeg)jq- for parsing JSON output from ffprobeUsage
input_file: Path to your video filestart_chapter: Chapter number to start from (starting at 1)end_chapter: Chapter number to end at (inclusive)Examples
Output
The script creates a new file in the same directory as the input file, with the naming pattern:
For example:
video_ch2-4.mkvNotes