Skip to content

Instantly share code, notes, and snippets.

@donnaken15
Last active August 30, 2025 02:45
Show Gist options
  • Select an option

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

Select an option

Save donnaken15/ccc1d1197b9a011d49cb28f2f1196824 to your computer and use it in GitHub Desktop.
Embed YouTube timestamps/Audacity label track as chapters in audio
#!/bin/zsh
[ $# -lt 1 -a $# -gt 2 ] && {
echo "Inadequate amount of arguments" 1>&2
exit 1
}
[ ! -f "$1" ] && {
echo "File does not exist" 1>&2
exit 1
}
tab=$'\t'
dig='[0-9]+'
dec="$dig\.$dig"
cr=$'\r'
lf=$'\n'
ds=$'\$'
i=0
fmt="($dig):($dig):($dig)(\.$dig)?"
timecode="%d:%02d:%06.3f"
[ "$1" -ef "$2" ] && set -- "$1"
stofmt() {
[[ "$1" =~ "^$fmt$ds" ]] && {
printf "$timecode" "${match[1]}" "${match[2]}" "$((${match[3]}+${match[4]:-0.000}))"
return 0
}
! [[ "$1" =~ "^$dec$ds" ]] && {
echo "Invalid input: $1" 1>&2
return 1
}
t="$(printf "%0.3f" "${match[1]}")" # avoid writing 0:60.000 :l
printf "$timecode" $(($t/3600)) $(($t/60%60)) $(($t%60))
return 0
}
out="${2:-${1:t:r}_chapters.${1:t:e}}"
{
while read -r inp; do
! [[ "$inp" =~ "^($fmt)[ $tab]([^$cr$lf]+)" ]] && {
! [[ "$inp" =~ "^($dec)$tab$dec$tab([^$cr$lf]+)" ]] && {
printf "Invalid line: %s" "$inp$lf" 1>&2
continue
}
}
printf "CHAPTER%03d=%s\nCHAPTER%03dNAME=%s\n" $i "$(stofmt "$match[1]")" $i "${match[6]:-${match[2]}}"
((i++))
done
# FFMPEG MAKES ME SUICIDAL
#while [ $i -lt 1000 ]; do # erase previous ones...
# printf "CHAPTER%03d=\nCHAPTER%03dNAME=\n" $i $i
# ((i++))
#done
} | ffmpeg -hide_banner -loglevel ${mverbose:-info} -i "$1" \
-f ffmetadata -i - -c copy -map_chapters -1 -map_metadata 1:g "$out" -y 2>&1 | \
grep -Uv -e '\s\+CHAPTER[0-9]\+' -e '^$' --color=always && touch "$out" -r "$1"
#!/bin/zsh
[ $# -lt 1 -a $# -gt 2 ] && {
echo "Inadequate amount of arguments"
exit 1
}
[ ! -f "$1" ] && {
echo "File does not exist" 1>&2
exit 1
}
tmpf="medit$RANDOM$RANDOM - ${1:t:r}"
ffprobe -v 0 -i "$1" -of json=compact=1 -show_chapters | \
jq -r '.chapters // {} | to_entries | map(
.value.start as $start |
($start%60000/1000|tostring|split(".")) as $S |
.value.tags.title as $title |
((("0"+(((($start)/(60*60*1000))|floor|tostring))|.[-2:])))+":"+
(("00"+(($start%(60*60*1000)/60000|floor|tostring))|.[-2:]))+":"+
(("0"+$S[0])|.[-2:])+"."+((($S[1]//"0")+"00")|.[:3])+
" "+$title
) | join("\n")' -> "$tmpf" && ${EDITOR:-nano} "$tmpf" && markers "$1" "${2:-$tmpf.${1:t:e}}" < "$tmpf" && rm "$tmpf" && \
(wait.exe 50 && < "$tmpf.${1:t:e}" > "$1" 2>/dev/null && touch "$1" -r "$tmpf.${1:t:e}"; rm "$tmpf.${1:t:e}" 2>/dev/null) # mverbose=error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment