Skip to content

Instantly share code, notes, and snippets.

@donnaken15
Last active November 21, 2025 16:09
Show Gist options
  • Select an option

  • Save donnaken15/98e665de860e95521f81c1fdb3907a8c to your computer and use it in GitHub Desktop.

Select an option

Save donnaken15/98e665de860e95521f81c1fdb3907a8c to your computer and use it in GitHub Desktop.
speed up videos slightly and cut silence audacity-style for skimming commentary slop
#!/bin/zsh
#set -e # exits early without pressing anything, POS
((# == 0)) && { # LOL # $((?)) also works, amazing
echo "No files entered"
exit 1
}
# accodomate for instances of <> 30fps or figure out how to change fps before processing my full graph
hms=(01 22 05); (( speed=1.18, limit=9999999, delay=((hms[1]*3600)+(hms[2]*60)+hms[3]) )) # seek to
unset hms; limit=5400 # seconds to shorten for skimming (testing)
fargs=( # tfw draft preset
-stats -stats_period 0.01 -c:v libvpx-vp9 -pix_fmt yuv444p -aspect 16:9
-crf 43 -qmin 28 -qmax 45 -vb 1100k -minrate 40k -maxrate 2500k -g 900 -bf 64 -refs 8
-threads 16 -tile-columns 6 -tile-rows 2 -row-mt 1 -deadline best -speed -16 -cpu-used -8
-tune ssim -tune-content screen -corpus-complexity 10000 -enable-tpl 1 -rc_lookahead 25
-frame-parallel 0 -aq-mode 0 -arnr-strength 4 -arnr-maxframes 7 -arnr-type 3 -sharpness 4
-static-thresh 0 -color_primaries bt709 -color_trc bt709 -colorspace bt709 -auto-alt-ref 1
-color_range pc -bufsize 512M -lag-in-frames 25 -fflags +genpts+bitexact
-flags +qpel+mv4+aic+global_header -map_metadata -1 -ar 24k -c:a libopus -ab 66k
-frame_duration 120 -vbr:a on -flags:a +bitexact -compression_level:a 10 -quality best
-application voip -flush_packets 1 -max_muxing_queue_size 1 -fflags nobuffer -blocksize 2048
)
cr=$'\r'; lf=$'\n'; ins='$b + { $k: ($v|tonumber? // .) }'
imark='\[(silencedetect|Parsed_blackframe_[0-9]+) @ [0-9a-f]{16}\]'
kv='( ([A-Za-z0-9_]+): ?([^ ]+))?'$cr?; stupid='( \|)?'; god="$stupid$kv"; temp='{}'
comp='volume=-2dB,dynaudnorm' # broken: acompressor=threshold=-9dB:ratio=5.1:attack=0.2:release=1.0:makeup=1
#trap 'exit 1' INT TERM # leaves ffmpeg open # use kill -9 `jobs` if seriously
sthres='-20.0dB'; ((sdur=0.16,srem=0.2,nick=0)); # figure this out like audacity duration and truncate to
((m_notice = 10.0)) # heads up warning (not using yet)
((m_lead = 0.3)) # seconds before/after detected frames
((m_trail = 1.9)) # minimal time skip (from last frame) because fatty has to get in that stupid word
((m_dist = 4.0)) # allowable distance between seconds before creating new cut, this should be a little more than reasonable
((cut_res = 30.0)) # trying hard to avoid video drifting, unironically doing more damage with more "bandaids"
# ive never had such strong contempt in my life for something as (ironically) insignificant as a freaking adjective
# and its all because of you zoomer ******s, im on team flam now, oreo should neck
bthres='blend=difference,blackframe=94:38' # trailing 200ms visibility left with this
afix='pan=mono|c0=c0+c1,aresample=32k'
profile=()
#profile=(time -f"%E") # not useful rn, keeps showing 0.01s, probably if subprocess management stuff isn't actually accounted for
# cut processing literally zooms when video is disabled, like a zoomer.........
echo "\x1b[91mKILL FFMPEG IF IT GETS STUCK, BECAUSE IT'S BAD SOFTWARE!!!!!!\x1b[0m"
alias readinto="tr -d '\r' | read -r -d ''"
for a in "$@"; do
[ ! -f "$a" ] && {
echo "Invalid or non-existent file: $a"
continue
}
base="`ffprobe -hide_banner -i "$a" -show_streams -print_format json | jq -c --arg delay "$delay" --arg limit "$limit" '{ off: (\$delay | tonumber), len: (\$limit | tonumber), sil: [], mas: [], str: .streams }'`"
jq -nr --argjson base "$base" 'first($base.str.[] | select(.codec_type == "video")).index' | readinto v
jq -ne --argjson base "$base" --argjson i "$v" '$base.str[$i].codec_type != "video"' >/dev/null && {
echo "This file does not have a video stream"
jq -n --indent 4 --argjson base "$base" '$base.str'
continue
}
cut_res="$(jq -nr --argjson base "$base" --argjson i "$v" '$base.str[$i].r_frame_rate' | tr -d '\r')" && \
echo "FPS eval: $cut_res" && cut_res=$(($cut_res)) || {
echo "Something went wrong when parsing frame rate"
jq -n --indent 4 --argjson base "$base" '$base.str'
continue
}
sil=(); mas=(); tc=0.0 # total time cut
scr="[0:a]$afix,$comp,silencedetect=n=$sthres:d=$sdur;"
# disable if not skimming nick's vods
# 999999iq amogus: regex check to determine whose title is who's
# now that i think about it, graph time shouldnt be noticeable if printing is
# nonblocking, so it'll keep going while the read loop takes its sweet @$$ time
if ((nick!=0)); then
scr+="[1][0:v]scale2ref[why1][trash1];[trash1][why1]$bthres"
why=( -t "$limit" -i "$a" -loop 1 -t "$limit" -i 'C:/dummy_target.jpg')
else
#scr+="[1]nullsink"
why=(-vn -t "$limit" -i "$a")
fi
echo "offset ${delay}:skim ${limit}s"
ffmpeg -hide_banner -hwaccel dxva2 -v info -nostats -ss "$delay" "${why[@]}" -lavfi "$scr" \
-f null nul 2>&1 | while read -r i; do
# old: 96:32
if [[ "${i//$cr/}" =~ ${imark}${kv}${god}${god}${god}${god}${god} ]]; then
filt="${match[1]}"
#echo "--- $filt ---"
case "$filt" in
Parsed_blackframe_[0-9]*) ;&
silencedetect)
for ((y=0;y<6;y++)); do
((x=3+(y*4)))
k="${match[$x]}"; v="${match[$((++x))]}"
[ -z "$k" ] && break; [ "$k" = 'silence_start' -o "$k" = 'frame' ] && temp='{}'
[[ "$k" = silence_* ]] && { # redundant
"${profile[@]}" jq -nc --arg k "${k:8}" --arg v "$v" --argjson b "$temp" "$ins" | readinto temp
# inb4 associative array and fromentries to save time from subprocesses when building up item
# or entries
[ "$k" = 'silence_duration' ] && {
cut=($(jq -ncr --argjson r "$srem" --argjson j "$temp" '$j | [.start, .end, ([$r,(.duration - $r)] | max)] | join(" ")' | tr -d '\r'))
"${profile[@]}" printf 'silence \x1b[92m%9.3f\x1b[97m ==> \x1b[92m%9.3f\x1b[0m, \x1b[97mcut \x1b[96m%6.3fs\x1b[0m, total: \x1b[95m%8.3f\x1b[0m\n' "${cut[@]}" "$((tc+=(cut[3])))"
sil+=("$temp")
}
continue
}
[[ "$filt" = Parsed_blackframe_[0-9]* ]] && {
"${profile[@]}" jq -nc --arg k "$k" --arg v "$v" --argjson b "$temp" "$ins" | readinto temp
[ "$k" = 'last_keyframe' ] && {
"${profile[@]}" jq -ncr --argjson hate 1000 --argjson j "$temp" '$j |
"\u001b[97mBAD: \u001b[91m"+
(.t | strftime("%H:%M:%S.")+((.*$hate%$hate+$hate)|tostring|.[1:]))+
"\u001b[0m"'
mas+=("$temp")
}
continue
}
echo "?????: $k = $v"
done
;;
*) echo "Uncaught $filt: $MATCH"
;;
esac
else
if ! [[ "${i:l}" =~ ^(metadata|m(aj|in)or_|encoder|output \#|stream \#[0-9]:[0-9](\[[0-9]x[0-9]\([a-z]*\)\])?: [av]|duration: |handler_|vendor|compati) ]]; then
printf "\x1b[90m%s\x1b[0m\n" "${i//$cr/}"
fi
fi
done
why=(); ((start=-1,end=-1,hate=100000,m_dist*=hate))
jq -nc -r '$ARGS.positional | .[].t' --jsonargs "${mas[@]}" | tr -d $'\r' | while read -r t; do
((start<(t*=hate)-m_dist)) && {
((start!=-1)) && why+=("$start $end"); ((start=t))
}; ((end=t))
done; ((start!=-1)) && why+=("$start $end")
jq -nc --argjson hate "$hate" --argjson r "$srem" --argjson j "$base" \
'$j|.sil = ($ARGS.positional | map([.start, .end, ([$r,.duration] | min/2)]))' \
--jsonargs "${sil[@]}" | \
jq -c --argjson hate "$hate" '.mas = ($ARGS.positional | map(. | split(" ")))' --args "${why[@]}" | readinto base
#jq -nc --tab --argjson j "$base" '$j'
date --date="TZ=\"%TZ%\" now" +'%F_%H-%M-%S.%3N' | readinto salt
echo 'writing detected data'; jq -nc --argjson j "$base" '$j' > "c:/skim_$salt.json"
echo 'generating cut points'; trim="select='not($(jq -ncr \
--argjson hate "$hate" --argjson ml "$m_lead" --argjson tb "$cut_res" \
--argjson j "$base" --argjson mt "$m_trail" \
'$j | .sil | map(
"between(t," +
(((.[0] + .[2]) * $tb | round / $tb) * $hate | floor / $hate | tostring) + "," +
(((.[1] - .[2]) * $tb | round / $tb) * $hate | floor / $hate | tostring) +
")"
) | . + (
$j | .mas | map(
"between(t," +
(.[0] - $ml | tostring) + "," +
(.[1] + $mt | tostring) +
")"
)
) + ["0"] | "\n"+join("+")' | tr -d '\r'
))'" # halve silence buffer time and distribute (forgot what to write for why...)
piss='setpts=N/FRAME_RATE/TB'
# life saver: https://stackoverflow.com/questions/64866231
# everyone/everything else is so unhelpful and useless
((nick!=0)) && block="drawbox=x=iw-w:y=ih-h+2:w=(iw*(1-0.66)):h=(ih*(1-0.58))+2:t=fill:c=red,"
echo 'rendering (no printing yet = seeking, maybe...)' # crop=480:130:0:0
tee "c:/skim_$salt.filt.txt" <<!! | ffmpeg -hide_banner -hwaccel dxva2 -v verbose -stats -ss "$delay" -i "$a" -t "$(((limit/speed)-tc))" -filter_complex_script pipe: "${fargs[@]}" "c:/skim_$salt.webm" -y
[0:v]scale=-1:360,${block}
drawtext=text='%{pts\:hms}':fontfile=e\\\:/Gossix.ttf:fontsize=32:fontcolor=white:x=0:y=16:box=1:[email protected],
${trim},setpts=N/FRAME_RATE/TB,setpts=PTS/$speed;
[0:a]$afix,aformat=f=flt,a${trim},asetpts=N/FRAME_RATE/TB,atempo=$speed,$comp;
!!
# KILLS ITSELF AROUND 8 MINUTES (THE CRINGE IS THAT BAD), NO CRASH DUMP?!
# offset pts
done
# %{localtime}
# -vf "scale=480:-1,drawbox=x=iw-w:y=ih-h+2:w=(iw*(1-0.66)):h=(ih*(1-0.58))+2:t=fill:c=red,
# drawtext=text='%{pts\:hms}':fontfile=e\\\:/Gossix.ttf:fontsize=32:fontcolor=white:x=8:y=8:box=1:[email protected],
# ${trim},${piss},setpts=PTS/$speed" \
# -af "pan=mono|c0=c0+c1,aresample=32k,aformat=f=flt,a${trim},a${piss},atempo=$speed,$comp"
# ffmpeg -hide_banner -hwaccel dxva2 -stats -ss 5:00 -i "F:\New folder\The Worst iDubbbz Interview Ever , Caleb Hammer VS Jake Weddle 2 & Pokimane [AULY1qhyRRQ].mp4" -loop 1 -i C:\dummy_target.jpg -an -lavfi "[0][1]scale2ref,blend=difference,blackframe=96:32" -f null nul
# ffmpeg -hide_banner -hwaccel dxva2 -stats -ss 10:00 -i "Ethan Klein's Full Response To Ian Jomha (iDubbbz) [Anisa Jomha's Husband] [fnHgShT1qlE].mp4" -loop 1 -i C:\dummy_target.jpg -an -lavfi "[0][1]scale2ref,blend=difference,blackframe=96:32" -f null nul
# ffprobe -v info -f lavfi -i "movie=die_foss:sp=600:dec_threads=15,select=gte(n\,0)[trash_stream];movie='c\:\\dummy_target.jpg',loop=loop=-1:size=1:start=0[nhyper];[trash_stream][nhyper]scale2ref,blend=difference,blackframe=96:32" -show_frames -print_format json | grep blackframe
# minimal case: 00:41:23.377 -- 00:41:25.458
#-ss "$delay" -t "$limit" -i "$a" -loop 1 \
# -i 'C:\dummy_target.jpg' -i 'C:\dummy_target2.jpg' -lavfi "
# [0:a]$comp,silencedetect=n=-25dB:d=0.16,anullsink;
# [1][0:v]scale2ref[why1][trash1];
# [2][trash1]scale2ref[why2][trash2];
# [trash1][why1]$bthresh,nullsink;
# [trash2][why2]$bthresh
#" -f null nul 2>&1 | while read -r i; do
# USES 7 GB COMMIT JUST FOR A SECOND COMPARISON FRAME WTFFFFFFF
# probably useless: sha256sum skim.json | cut -d' ' -f1 | xxd -r -p | base64 -w 110
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment