Skip to content

Instantly share code, notes, and snippets.

@Vftdan
Created July 16, 2023 21:17
Show Gist options
  • Save Vftdan/b2e9cb242316f3587ddebb6afa9a4a6c to your computer and use it in GitHub Desktop.
Save Vftdan/b2e9cb242316f3587ddebb6afa9a4a6c to your computer and use it in GitHub Desktop.
Owncast chat log to ASS/SSA subtitles
#! /bin/sh
# owncast-chat2ass.sh
# Copyright (c) 2023 vftdan (https://github.com/Vftdan)
TimeToSeconds() {
date -d "$1" +'%s.%N'
}
TimespanSeconds() {
printf '%s\n' "$(TimeToSeconds "$2") - $(TimeToSeconds "$1")" | bc
}
SecondsToTimeWithCS() {
# CS = centisecond
# Wraps at 24h!!!
date -ud "@$1" +'%T.%N' | sed -E 's/^0?//; s/[0-9]{7}$//'
}
EscapeRtf() {
sed -E 's/[\\{}]/\\\0/g; s/$/\\n/g' | tr -d '\n' | head -c -2
echo
}
HtmlToRtf() {
sed -E 's/<(em|i)>/{\\i1}/g; s/<\/(em|i)>/{\\i0}/g; s/<(b|strong)>/{\\b1}/g; s/<\/(b|strong)>/{\\b0}/g; s/\&gt;/>/g; s/\&lt;/</g; s/\&\#39;/'"'"'/g; s/\&quot;/"/g; s/\&amp;/\&/g'
}
SanitizeSSACSVValue() {
tr -d ',\n'
}
PrintSSACSVSanitizedValue() {
printf '%s,' "$(printf '%s\n' "$1" | SanitizeSSACSVValue)"
}
ExtractMessages() {
jq -c '.[] | select(.type == "CHAT") | {"timestamp": .timestamp, "user": .user.displayName, "text": .body}'
}
PrintSSAINISection() {
printf '[%s]\n' "$1"
}
PrintSSAINIKeyPrefix() {
printf '%s: ' "$1"
}
PrependLinesSSAINIKey() {
while read -r line; do \
PrintSSAINIKeyPrefix "$1"
printf '%s\n' "$line"
done
}
MessageToSSAEventCSV() {
local timestamp="$(printf "%s" "$2" | jq -r '.timestamp')"
local user="$(printf '%s' "$2" | jq -r '.user')"
local text="$(printf '%s' "$2" | jq -r '.text')"
local secfromstart="$(TimespanSeconds "$vodstart" "$timestamp")"
local endsecfromstart="$(printf '%s\n' "$secfromstart + $1" | bc)"
# Layer:
PrintSSACSVSanitizedValue 0
# Start:
PrintSSACSVSanitizedValue "$(SecondsToTimeWithCS "$secfromstart")"
# End:
PrintSSACSVSanitizedValue "$(SecondsToTimeWithCS "$endsecfromstart")"
# Style:
PrintSSACSVSanitizedValue Default
# Name:
PrintSSACSVSanitizedValue "$user"
# MarginL:
PrintSSACSVSanitizedValue 0
# MarginR:
PrintSSACSVSanitizedValue 0
# MarginV:
PrintSSACSVSanitizedValue 0
# Effect:
PrintSSACSVSanitizedValue 'Scroll up;300;200;25'
# Text:
printf '%s: %s\n' "$user" "$text" | EscapeRtf | HtmlToRtf
}
FlatMapLines() {
while read -r line; do \
"$@" "$line"
done
}
Main() {
PrintSSAINISection "Script Info"
echo '; Script generated by owncast-chat2ass.sh'
echo '; Shellscript author: https://github.com/Vftdan'
PrintSSAINIKeyPrefix Title
echo 'Default Aegisub file'
PrintSSAINIKeyPrefix ScriptType
echo 'v4.00+'
echo
PrintSSAINISection Events
PrintSSAINIKeyPrefix Format
echo 'Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text'
ExtractMessages | FlatMapLines MessageToSSAEventCSV 2 | PrependLinesSSAINIKey Dialogue
}
PrintUsage() {
echo "Usage: $0 <vod_start_timestamp>"
echo 'VOD start timestamp must be provided in the format understood by date(1)'
echo 'JSON file in the format like returned by https://${OWNCAST_SERVER}/api/chat?accessToken=${YOUR_ACCESS_TOKEN} must be piped into the standard input'
echo 'Minimal SSA/ASS subtitle file will be printed to the standard output'
}
if [ $# -ne 1 ]; then \
PrintUsage >&2
exit 1
fi
case "$1" in
-h|--help)
PrintUsage
exit 0
;;
esac
export vodstart="$1"
Main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment