weavesignal/tests/demo-video/check.sh
The WeaveSignal contributors 16833c96ae The demo video's tooling, the video on the website, and a Cloudron changelog the listing can read
- tests/demo-video/: the script, designed voices, the run through the real desk and the edit that
  make the website's demo video (the media themselves are not in git).
- The home page plays the video, with its credits.
- deploy/cloudron/CHANGELOG.md: one line per entry; CloudronVersions.json rebuilt.
2026-11-04 16:22:47 +00:00

67 lines
4 KiB
Shell
Executable file

#!/usr/bin/env bash
# SPDX-License-Identifier: AGPL-3.0-only
# SPDX-FileCopyrightText: 2026 The WeaveSignal contributors
#
# Listens to the finished demo video: every line is cut from the video's own soundtrack where
# the edit put it and given to VoiceLab's Whisper (127.0.0.1:8181), and what it heard is set
# against the script (a lagging or clipped line, or one drowned by a cart, reads as a miss);
# the swear must not be heard anywhere, neither in the video (bleeped) nor in the listener
# stream around the dump (dumped); and the loudness of each file is measured (-16 LUFS
# integrated is the aim). It also writes a frame of each moment to target/demo-video/edit/check/.
#
# tests/demo-video/check.sh [video] # default target/demo-video/out/demo.mp4
set -euo pipefail
here=$(cd "$(dirname "$0")" && pwd)
root=$(cd "$here/../.." && pwd)
script="$here/script.json"
. "$here/words.sh"
video=${1:-$root/target/demo-video/out/demo.mp4}
tv="$root/target/demo-video/edit/timeline-video.json"
STT=${STT:-http://127.0.0.1:8181}
work="$root/target/demo-video/edit/check"
mkdir -p "$work"
hear() { curl -fsS --max-time 120 -F "file=@$1" -F response_format=json -F temperature=0 "$STT/v1/audio/transcriptions" | jq -r .text | tr '\n' ' ' | sed 's/^ *//; s/ *$//'; }
misses=0
while IFS= read -r l; do
id=$(jq -r .id <<<"$l"); at=$(jq -r .at <<<"$l"); len=$(jq -r .len <<<"$l"); text=$(jq -r .text <<<"$l"); swear=$(jq -r '.swear // ""' <<<"$l")
ffmpeg -v error -y -ss "$(echo "$at - 0.15" | bc)" -t "$(echo "$len + 0.4" | bc)" -i "$video" -vn -ac 1 -ar 16000 "$work/$id.wav"
heard=$(hear "$work/$id.wav")
want=$text
if [ -n "$swear" ]; then
if grep -qiw "$swear" <<<"$heard"; then echo "SWEAR HEARD in the video: $id: $heard"; misses=$((misses + 1)); fi
# The words are checked up to the bleep (Whisper invents words for a tone), less the word
# before the swear, whose last sound the bleep's edge may take.
upto=$(jq -r .swear_from <<<"$l")
ffmpeg -v error -y -ss "$(echo "$at - 0.15" | bc)" -t "$(echo "$upto + 0.1" | bc)" -i "$video" -vn -ac 1 -ar 16000 "$work/$id-before.wav"
heard=$(hear "$work/$id-before.wav")
want=$(sed -E "s/[[:alnum:]']+ +$swear\\b.*$//" <<<"$text")
fi
a=$(norm "$want"); b=$(norm "$heard")
# Up to the bleep, Whisper may stop a word or two short of it: compare as many words as it heard.
[ -n "$swear" ] && a=$(cut -d' ' -f1-"$(wc -w <<<"$b")" <<<"$a")
d=$(distance "$a" "$b")
# A word or one in ten may go to Whisper's own slips (a name split, a soft last consonant).
words=$(wc -w <<<"$a"); allowed=$(( words / 10 > 1 ? words / 10 : 1 ))
mark=ok; [ "$d" -gt 0 ] && mark="ok ($d)"; [ "$d" -gt "$allowed" ] && { mark="MISS ($d)"; misses=$((misses + 1)); }
printf '%-14s %7.2fs %-9s %s\n' "$id" "$at" "$mark" "$heard"
done < <(jq -c '.lines[]' "$tv")
# The listener stream around the dump: the swear must not be in it.
swear=$(jq -r '[.moments[].steps[] | select(.swear) | .swear][0]' "$script")
sfile=$(jq -r .stream.file "$tv"); sfrom=$(jq -r .stream.dump.from "$tv"); slen=$(jq -r .stream.dump.len "$tv")
ffmpeg -v error -y -ss "$sfrom" -t "$slen" -i "$sfile" -ac 1 -ar 16000 "$work/stream-dump.wav"
heard=$(hear "$work/stream-dump.wav")
if grep -qiw "$swear" <<<"$heard"; then echo "SWEAR HEARD on the listener stream: $heard"; misses=$((misses + 1)); else echo "listener stream around the dump (no \"$swear\"): $heard"; fi
for f in "$video" "$root"/target/demo-video/out/demo.webm "$root"/target/demo-video/out/clips/*.webm; do
[ -f "$f" ] || continue
i=$(ffmpeg -hide_banner -i "$f" -map 0:a -af ebur128=peak=true:framelog=quiet -f null - 2>&1 | awk '/Integrated loudness/{getline; print $2} /True peak/{getline; print "peak " $2}' | tr '\n' ' ')
printf '%-40s %s s %s LUFS\n' "$(basename "$f")" "$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$f")" "$i"
done
while IFS= read -r m; do
id=$(jq -r .id <<<"$m"); at=$(jq -r .at <<<"$m")
ffmpeg -v error -y -ss "$(echo "$at + 3" | bc)" -i "$video" -frames:v 1 -vf scale=960:540 "$work/moment-$id.jpg"
done < <(jq -c '.moments[]' "$tv")
echo "$misses misses"
[ "$misses" = 0 ]