Build a local tool that slices videos into clips using Python and FFmpeg.
Welcome to another official UVF IT build guide. Today we are creating a minimal working clone of VideoHighlighter, focusing specifically on its core capability: extracting specific segments from a video file.
Instead of relying on heavy AI models for scene detection, we will build a simple, deterministic tool that lets you define start and end times to cut out highlights. This approach is perfect for understanding how FFmpeg works under the hood without the complexity of machine learning.
brew install ffmpeg or apt install ffmpeg).Create a new directory for our project and initialize a Python environment. We will use a simple main.py script to handle the logic.
Ensure FFmpeg is accessible by checking its version in your terminal.
mkdir video-clipper
cd video-clipper
ffmpeg -versionWe need a way to accept user input for the video path, start time, and end time. We will use argparse to handle command-line arguments.
Add basic validation to ensure the start time is less than the end time and that the input file exists.
main.py
import argparse
import os
import sys
def parse_args():
parser = argparse.ArgumentParser(description="Extract a clip from a video file.")
parser.add_argument("input_file", help="Path to the source video file")
parser.add_argument("-s", "--start", type=float, required=True, help="Start time in seconds")
parser.add_argument("-e", "--end", type=float, required=True, help="End time in seconds")
parser.add_argument("-o", "--output", default="my_clip.mp4", help="Output file name")
return parser.parse_args()
def validate_input(input_file, start, end):
if not os.path.exists(input_file):
raise FileNotFoundError(f"Input file '{input_file}' does not exist.")
if start < 0 or end < 0:
raise ValueError("Start and end times must be non-negative.")
if start >= end:
raise ValueError("Start time must be less than end time.")
def main():
args = parse_args()
try:
validate_input(args.input_file, args.start, args.end)
except (FileNotFoundError, ValueError) as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
print(f"Validated: {args.input_file} from {args.start}s to {args.end}s")
# Placeholder for FFmpeg execution in Phase 3
if __name__ == "__main__":
main()Now we connect to the real workhorse: FFmpeg. We will use Python's subprocess module to execute the FFmpeg command.
The key flags are -ss (start time), -to (end time), and -c copy to avoid re-encoding, which makes the process very fast.
main.py
import argparse
import os
import sys
import subprocess
def parse_args():
parser = argparse.ArgumentParser(description="Extract a clip from a video file.")
parser.add_argument("input_file", help="Path to the source video file")
parser.add_argument("-s", "--start", type=float, required=True, help="Start time in seconds")
parser.add_argument("-e", "--end", type=float, required=True, help="End time in seconds")
parser.add_argument("-o", "--output", default="my_clip.mp4", help="Output file name")
return parser.parse_args()
def validate_input(input_file, start, end):
if not os.path.exists(input_file):
raise FileNotFoundError(f"Input file '{input_file}' does not exist.")
if start < 0 or end < 0:
raise ValueError("Start and end times must be non-negative.")
if start >= end:
raise ValueError("Start time must be less than end time.")
def extract_clip(input_file, start, end, output_file):
# -ss before -i for fast seeking
# -to specifies the stop time
# -c copy avoids re-encoding (fast but less precise cuts)
cmd = [
"ffmpeg",
"-y", # Overwrite output file without asking
"-ss", str(start),
"-i", input_file,
"-to", str(end),
"-c", "copy",
output_file
]
try:
subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(f"Successfully extracted clip to '{output_file}'")
except subprocess.CalledProcessError as e:
print(f"FFmpeg failed with return code {e.returncode}", file=sys.stderr)
print(f"STDERR: {e.stderr.decode()}", file=sys.stderr)
sys.exit(1)
def main():
args = parse_args()
try:
validate_input(args.input_file, args.start, args.end)
except (FileNotFoundError, ValueError) as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
extract_clip(args.input_file, args.start, args.end, args.output)
if __name__ == "__main__":
main()Before we can test our extraction logic, we need a source video file. If you do not have one handy, you can download a small sample MP4 from a public test video repository or use any short video on your local machine. Ensure the file is under 100MB to keep processing times manageable.
Now we are ready to execute our script. We will pass the path to the source video, along with the start and end times for the clip we want to extract. For this example, we will extract a 5-second segment starting at the 10-second mark.
# Download a small test video if you don't have one
# (Example using a placeholder URL, replace with a valid small mp4 link)
# curl -o sample.mp4 https://www.w3schools.com/html/mov_bbb.mp4
# Run the extractor: extract from 10 seconds to 15 seconds
python main.py sample.mp4 -s 10 -e 15 -o my_clip.mp4To confirm that our tool worked correctly, we should inspect the output file. We can use ffprobe, which is part of the FFmpeg suite, to check the duration of the newly created clip. It should be approximately 5 seconds long.
Once you have verified the basic functionality, you can start thinking about extensions. A common next step is to add a feature that lists all existing clips in the output directory, or to wrap this logic in a simple Flask web application to provide a user-friendly interface.
# Verify the duration of the output clip
# The output should show a duration close to 5.000000
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 my_clip.mp4| Check | Command / Why it matters |
|---|---|
| FFmpeg is installed | Run ffmpeg -version in terminal. You should see version info, not a 'command not found' error. |
| Script validates input | Run python main.py nonexistent.mp4 -s 0 -e 5. It should raise a FileNotFoundError. |
| Clip is extracted | Run the extraction command. A new file my_clip.mp4 should appear in the directory. |
| Clip duration is correct | Run ffprobe on the output. The duration should be approximately 5 seconds (end - start). |
You now have a working video clip extractor! To make it more like VideoHighlighter, consider adding a simple web interface with Flask or FastAPI, or integrating a lightweight scene detection library like scenedetect to automatically suggest highlight moments.