Last active
April 13, 2025 05:50
-
-
Save krasi-georgiev/1f167a2136bbce5824c6516d2777ef79 to your computer and use it in GitHub Desktop.
Cut a video file from frame.io comments.
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 | |
# Run with: | |
# bash <(curl -sL "https://gist.githubusercontent.com/krasi-georgiev/1f167a2136bbce5824c6516d2777ef79/raw/cut_segments.sh") | |
shopt -s nullglob | |
### STEP 1: Select CSV file | |
csv_files=( *.csv ) | |
if [ ${#csv_files[@]} -eq 0 ]; then | |
echo "❌ No CSV files found." | |
exit 1 | |
fi | |
if [ -t 0 ]; then | |
echo "Available CSV files:" | |
select csv_file in "${csv_files[@]}"; do | |
[[ -n "$csv_file" ]] && break | |
echo "❌ Invalid selection. Try again." | |
done | |
else | |
csv_file="${csv_files[0]}" | |
echo "ℹ️ Auto-selected CSV: $csv_file" | |
fi | |
### STEP 2: Select MP4 file | |
mp4_files=( *.mp4 ) | |
if [ ${#mp4_files[@]} -eq 0 ]; then | |
echo "❌ No MP4 files found." | |
exit 1 | |
fi | |
IFS=$'\n' mp4_files=( $(ls -S *.mp4) ) | |
if [ -t 0 ]; then | |
echo "Available MP4 files (sorted by size):" | |
select video_file in "${mp4_files[@]}"; do | |
[[ -n "$video_file" ]] && break | |
echo "❌ Invalid selection. Try again." | |
done | |
else | |
video_file="${mp4_files[0]}" | |
echo "ℹ️ Auto-selected MP4: $video_file" | |
fi | |
### STEP 3: Detect FPS | |
FPS=$(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate "$video_file" | bc -l) | |
if [[ -z "$FPS" || "$FPS" == "0" ]]; then | |
echo "❌ Could not detect FPS from video." | |
exit 1 | |
fi | |
echo "🎞 Detected FPS: $FPS" | |
### STEP 4: Extract Frame column (14) | |
echo "📋 Parsing frames from column 14..." | |
mapfile -t frames < <( | |
tail -n +2 "$csv_file" | awk -F',' ' | |
{ | |
gsub(/^"|"$/, "", $14) | |
if ($14 ~ /^[0-9]+$/) print $14 | |
} | |
' | |
) | |
### STEP 5: Process in pairs | |
total_pairs=$(( ${#frames[@]} / 2 )) | |
echo "🎬 Cutting $total_pairs segments from ${#frames[@]} frame entries..." | |
for (( i=0; i<${#frames[@]}-1; i+=2 )); do | |
start_frame="${frames[$i]}" | |
end_frame="${frames[$((i+1))]}" | |
if (( end_frame <= start_frame )); then | |
echo "❌ Error: Invalid segment. End frame ($end_frame) <= Start frame ($start_frame)" | |
exit 1 | |
fi | |
start_sec=$(bc <<< "scale=6; $start_frame / $FPS") | |
end_sec=$(bc <<< "scale=6; $end_frame / $FPS") | |
output_file="segment_${start_frame}_to_${end_frame}.mp4" | |
echo "⏱ Cutting: frame $start_frame ($start_sec s) → $end_frame ($end_sec s)" | |
echo "📁 Output: $output_file" | |
ffmpeg -hide_banner -loglevel error -y \ | |
-ss "$start_sec" -to "$end_sec" \ | |
-i "$video_file" -c copy "$output_file" | |
if [ $? -eq 0 ]; then | |
echo "✅ Created: $output_file" | |
else | |
echo "❌ Failed: $output_file" | |
fi | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment