Skip to content

Instantly share code, notes, and snippets.

@henrycunh
Created January 24, 2025 13:28
Show Gist options
  • Save henrycunh/9f48d177032e0201f34ca28461d63b64 to your computer and use it in GitHub Desktop.
Save henrycunh/9f48d177032e0201f34ca28461d63b64 to your computer and use it in GitHub Desktop.
Download YT Clip
#!/usr/bin/env bash
#
# ytclip: Download and clip a portion of a YouTube video into MP4.
#
# Usage:
# ytclip <YOUTUBE_URL> --from <TIME> --to <TIME>
#
# <TIME> can be:
# - S (e.g. "75")
# - M:S (e.g. "2:38")
# - H:M:S (e.g. "01:02:03")
#
# Example:
# ytclip "https://www.youtube.com/watch?v=7DV9SZgxegA" --from 2:38 --to 2:41
#
# Dependencies:
# - yt-dlp (https://github.com/yt-dlp/yt-dlp)
# - ffmpeg (https://ffmpeg.org/)
# ----------------------
# 1. Parse Arguments
# ----------------------
if [[ $# -lt 5 ]]; then
echo "Usage: $0 <YOUTUBE_URL> --from <TIME> --to <TIME>"
echo "Example: $0 \"https://youtu.be/XYZ\" --from 2:38 --to 2:41"
exit 1
fi
YOUTUBE_URL="$1"
shift
FROM_TIME=""
TO_TIME=""
while [[ $# -gt 0 ]]; do
case "$1" in
--from)
FROM_TIME="$2"
shift 2
;;
--to)
TO_TIME="$2"
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Ensure all required fields are present
if [[ -z "$YOUTUBE_URL" || -z "$FROM_TIME" || -z "$TO_TIME" ]]; then
echo "Error: Missing required arguments."
echo "Usage: $0 <YOUTUBE_URL> --from <TIME> --to <TIME>"
exit 1
fi
# ---------------------------------------
# 2. Convert Time Strings to Seconds
# ---------------------------------------
# This function accepts S, M:S, or H:M:S and converts to total integer seconds.
function hms_to_seconds() {
local timestr="$1"
local IFS=':'
local parts=($timestr) # Split on ':'
local len=${#parts[@]}
case $len in
1) # S
echo "$((10#${parts[0]}))"
;;
2) # M:S
echo "$((10#${parts[0]} * 60 + 10#${parts[1]}))"
;;
3) # H:M:S
echo "$((10#${parts[0]} * 3600 + 10#${parts[1]} * 60 + 10#${parts[2]}))"
;;
*)
echo "Invalid time format: $timestr" >&2
exit 1
;;
esac
}
START_SEC=$(hms_to_seconds "$FROM_TIME")
END_SEC=$(hms_to_seconds "$TO_TIME")
# ---------------------------------------
# 3. Calculate Duration & Validate
# ---------------------------------------
if (( END_SEC <= START_SEC )); then
echo "Error: End time must be greater than start time!"
exit 1
fi
DURATION_SEC=$((END_SEC - START_SEC))
# -----------------------
# 4. Prepare Output
# -----------------------
CLIPS_DIR="$HOME/clips"
mkdir -p "$CLIPS_DIR"
TIMESTAMP="$(date +'%Y%m%d_%H%M%S')"
TEMP_FILE="$CLIPS_DIR/temp_${TIMESTAMP}.mp4"
OUTPUT_FILE="$CLIPS_DIR/clip_${TIMESTAMP}.mp4"
# -----------------------
# 5. Download via yt-dlp
# -----------------------
echo "Downloading video to $TEMP_FILE..."
yt-dlp --merge-output-format mp4 "$YOUTUBE_URL" -o "$TEMP_FILE"
if [[ $? -ne 0 ]]; then
echo "Error: Download failed."
exit 1
fi
# -----------------------
# 6. Use ffmpeg to Clip
# -----------------------
echo "Clipping from $FROM_TIME to $TO_TIME (duration: ${DURATION_SEC}s)..."
# Notice we put -ss AFTER -i for more accurate seeking
ffmpeg -hide_banner -loglevel error \
-i "$TEMP_FILE" \
-ss "$START_SEC" \
-t "$DURATION_SEC" \
-c copy \
"$OUTPUT_FILE"
if [[ $? -ne 0 ]]; then
echo "Error: ffmpeg clipping failed."
exit 1
fi
# Remove the large downloaded video, keep only the clip
rm -f "$TEMP_FILE"
# -----------------------
# 7. Open Finder & Done
# -----------------------
open "$CLIPS_DIR"
echo "Done! Your clip is at: $OUTPUT_FILE"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment