2026-04-24 14:49:57 -03:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# scripts/manual/run_upstream_browser_av_sync.sh
|
2026-04-29 01:25:06 -03:00
|
|
|
# Manual: browser consumer A/V sync hardware probe; not part of CI.
|
2026-04-24 14:49:57 -03:00
|
|
|
#
|
|
|
|
|
# Drive a real browser consumer on Tethys, record the combined MediaStream,
|
|
|
|
|
# pull the capture back, and analyze it with the Lesavka sync analyzer.
|
|
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
|
|
|
|
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd)"
|
|
|
|
|
|
|
|
|
|
TETHYS_HOST=${TETHYS_HOST:-tethys}
|
2026-04-30 08:16:57 -03:00
|
|
|
LESAVKA_SERVER_ADDR=${LESAVKA_SERVER_ADDR:-https://38.28.125.112:50051}
|
2026-04-24 14:49:57 -03:00
|
|
|
PROBE_DURATION_SECONDS=${PROBE_DURATION_SECONDS:-15}
|
2026-05-01 02:05:07 -03:00
|
|
|
BROWSER_RECORD_SECONDS=${BROWSER_RECORD_SECONDS:-${PROBE_DURATION_SECONDS}}
|
|
|
|
|
BROWSER_SYNC_DRIVER_COMMAND=${BROWSER_SYNC_DRIVER_COMMAND:-}
|
|
|
|
|
SYNC_ANALYZE_EVENT_WIDTH_CODES=${SYNC_ANALYZE_EVENT_WIDTH_CODES:-}
|
2026-04-24 14:49:57 -03:00
|
|
|
BROWSER_PORT=${BROWSER_PORT:-18443}
|
|
|
|
|
REMOTE_SCRIPT=${REMOTE_SCRIPT:-/tmp/lesavka-browser-consumer-probe.py}
|
|
|
|
|
REMOTE_CAPTURE=${REMOTE_CAPTURE:-/tmp/lesavka-browser-av-sync.webm}
|
|
|
|
|
REMOTE_STATUS=${REMOTE_STATUS:-/tmp/lesavka-browser-av-sync-status.json}
|
|
|
|
|
REMOTE_PROFILE_DIR=${REMOTE_PROFILE_DIR:-/tmp/lesavka-browser-probe-profile}
|
|
|
|
|
LOCAL_OUTPUT_DIR=${LOCAL_OUTPUT_DIR:-"${REPO_ROOT}/tmp"}
|
|
|
|
|
SSH_OPTS=${SSH_OPTS:-"-o BatchMode=yes -o ConnectTimeout=5"}
|
|
|
|
|
DISPLAY_ENV=${DISPLAY_ENV:-":0"}
|
|
|
|
|
REMOTE_RUNTIME_DIR=${REMOTE_RUNTIME_DIR:-/run/user/1000}
|
|
|
|
|
REMOTE_DBUS_ADDRESS=${REMOTE_DBUS_ADDRESS:-}
|
|
|
|
|
REMOTE_XAUTHORITY=${REMOTE_XAUTHORITY:-}
|
|
|
|
|
READY_TIMEOUT_SECONDS=${READY_TIMEOUT_SECONDS:-120}
|
|
|
|
|
|
|
|
|
|
mkdir -p "${LOCAL_OUTPUT_DIR}"
|
|
|
|
|
STAMP="$(date +%Y%m%d-%H%M%S)"
|
|
|
|
|
LOCAL_CAPTURE="${LOCAL_OUTPUT_DIR}/lesavka-browser-av-sync-${STAMP}.webm"
|
2026-05-01 01:26:24 -03:00
|
|
|
LOCAL_REPORT_DIR="${LOCAL_OUTPUT_DIR}/lesavka-browser-av-sync-${STAMP}"
|
2026-04-24 14:49:57 -03:00
|
|
|
|
|
|
|
|
scp ${SSH_OPTS} "${REPO_ROOT}/scripts/manual/browser_consumer_probe.py" "${TETHYS_HOST}:${REMOTE_SCRIPT}"
|
|
|
|
|
|
|
|
|
|
ssh ${SSH_OPTS} "${TETHYS_HOST}" bash -s -- \
|
|
|
|
|
"${REMOTE_SCRIPT}" \
|
|
|
|
|
"${REMOTE_CAPTURE}" \
|
|
|
|
|
"${REMOTE_STATUS}" \
|
|
|
|
|
"${REMOTE_PROFILE_DIR}" \
|
2026-05-01 02:05:07 -03:00
|
|
|
"${BROWSER_RECORD_SECONDS}" \
|
2026-04-24 14:49:57 -03:00
|
|
|
"${BROWSER_PORT}" \
|
|
|
|
|
"${DISPLAY_ENV}" \
|
|
|
|
|
"${REMOTE_RUNTIME_DIR}" <<'REMOTE_SETUP'
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
remote_script=$1
|
|
|
|
|
remote_capture=$2
|
|
|
|
|
remote_status=$3
|
|
|
|
|
remote_profile_dir=$4
|
|
|
|
|
duration=$5
|
|
|
|
|
port=$6
|
|
|
|
|
display_env=$7
|
|
|
|
|
runtime_dir=$8
|
|
|
|
|
dbus_address=""
|
|
|
|
|
xauthority_path=""
|
|
|
|
|
firefox_pid="$(pgrep -n -x firefox-esr || true)"
|
|
|
|
|
if [[ -n "${firefox_pid}" && -r "/proc/${firefox_pid}/environ" ]]; then
|
|
|
|
|
while IFS='=' read -r key value; do
|
|
|
|
|
case "$key" in
|
|
|
|
|
DBUS_SESSION_BUS_ADDRESS) dbus_address="$value" ;;
|
|
|
|
|
XAUTHORITY) xauthority_path="$value" ;;
|
|
|
|
|
DISPLAY) [[ -z "${display_env}" || "${display_env}" == ":0" ]] && display_env="$value" ;;
|
|
|
|
|
esac
|
|
|
|
|
done < <(tr '\0' '\n' <"/proc/${firefox_pid}/environ")
|
|
|
|
|
fi
|
|
|
|
|
[[ -z "${dbus_address}" ]] && dbus_address="unix:path=${runtime_dir}/bus"
|
|
|
|
|
fuser -k "${port}/tcp" >/dev/null 2>&1 || true
|
|
|
|
|
pkill -f "firefox.*${remote_profile_dir}" >/dev/null 2>&1 || true
|
|
|
|
|
for _ in $(seq 1 20); do
|
|
|
|
|
if ! pgrep -f "firefox.*${remote_profile_dir}" >/dev/null 2>&1; then
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
sleep 0.25
|
|
|
|
|
done
|
|
|
|
|
rm -f "$remote_capture" "$remote_status"
|
|
|
|
|
rm -rf "$remote_profile_dir"
|
|
|
|
|
mkdir -p "$remote_profile_dir"
|
|
|
|
|
cat >"${remote_profile_dir}/user.js" <<'FIREFOX_PREFS'
|
|
|
|
|
user_pref("media.navigator.permission.disabled", true);
|
|
|
|
|
user_pref("permissions.default.camera", 1);
|
|
|
|
|
user_pref("permissions.default.microphone", 1);
|
|
|
|
|
user_pref("media.autoplay.default", 0);
|
|
|
|
|
user_pref("media.autoplay.blocking_policy", 0);
|
|
|
|
|
user_pref("toolkit.telemetry.reportingpolicy.firstRun", false);
|
|
|
|
|
user_pref("browser.shell.checkDefaultBrowser", false);
|
|
|
|
|
user_pref("browser.tabs.warnOnClose", false);
|
|
|
|
|
user_pref("browser.startup.page", 1);
|
|
|
|
|
user_pref("browser.startup.homepage_override.mstone", "ignore");
|
|
|
|
|
user_pref("startup.homepage_welcome_url", "");
|
|
|
|
|
user_pref("startup.homepage_welcome_url.additional", "");
|
|
|
|
|
user_pref("browser.aboutwelcome.enabled", false);
|
|
|
|
|
user_pref("trailhead.firstrun.didSeeAboutWelcome", true);
|
|
|
|
|
FIREFOX_PREFS
|
|
|
|
|
printf 'user_pref("browser.startup.homepage", "http://127.0.0.1:%s/");\n' "$port" >>"${remote_profile_dir}/user.js"
|
|
|
|
|
nohup python3 "$remote_script" --port "$port" --output "$remote_capture" --status "$remote_status" --duration-seconds "$duration" >/tmp/lesavka-browser-consumer-probe.log 2>&1 &
|
|
|
|
|
if [[ -n "${xauthority_path}" ]]; then
|
|
|
|
|
nohup env DISPLAY="$display_env" XDG_RUNTIME_DIR="$runtime_dir" DBUS_SESSION_BUS_ADDRESS="$dbus_address" XAUTHORITY="$xauthority_path" \
|
|
|
|
|
firefox --new-instance --no-remote --profile "$remote_profile_dir" \
|
|
|
|
|
>/tmp/lesavka-browser-consumer-firefox.log 2>&1 &
|
|
|
|
|
else
|
|
|
|
|
nohup env DISPLAY="$display_env" XDG_RUNTIME_DIR="$runtime_dir" DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
|
|
|
|
|
firefox --new-instance --no-remote --profile "$remote_profile_dir" \
|
|
|
|
|
>/tmp/lesavka-browser-consumer-firefox.log 2>&1 &
|
|
|
|
|
fi
|
|
|
|
|
REMOTE_SETUP
|
|
|
|
|
|
|
|
|
|
echo "==> waiting for browser consumer to become ready on ${TETHYS_HOST}"
|
|
|
|
|
deadline=$(( $(date +%s) + READY_TIMEOUT_SECONDS ))
|
|
|
|
|
while true; do
|
|
|
|
|
status_json=$(ssh ${SSH_OPTS} "${TETHYS_HOST}" "test -f '${REMOTE_STATUS}' && cat '${REMOTE_STATUS}'" || true)
|
|
|
|
|
if [[ -n "${status_json}" ]]; then
|
|
|
|
|
if STATUS_JSON="${status_json}" python3 -c 'import json, os, sys; status = json.loads(os.environ["STATUS_JSON"]); sys.exit(0 if status.get("ready") else 1)'
|
|
|
|
|
then
|
|
|
|
|
echo "==> browser consumer ready"
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
if (( $(date +%s) >= deadline )); then
|
|
|
|
|
echo "browser consumer did not become ready before timeout" >&2
|
|
|
|
|
[[ -n "${status_json:-}" ]] && echo "last status: ${status_json}" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
sleep 1
|
|
|
|
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
echo "==> triggering browser recording"
|
|
|
|
|
ssh ${SSH_OPTS} "${TETHYS_HOST}" "curl -fsS -X POST http://127.0.0.1:${BROWSER_PORT}/start >/dev/null"
|
|
|
|
|
|
|
|
|
|
sleep 1
|
|
|
|
|
|
2026-05-01 02:05:07 -03:00
|
|
|
if [[ -n "${BROWSER_SYNC_DRIVER_COMMAND}" ]]; then
|
|
|
|
|
echo "==> running custom browser sync driver"
|
|
|
|
|
bash -lc "${BROWSER_SYNC_DRIVER_COMMAND}"
|
|
|
|
|
else
|
|
|
|
|
echo "==> running local Lesavka sync probe against ${LESAVKA_SERVER_ADDR}"
|
|
|
|
|
(
|
|
|
|
|
cd "${REPO_ROOT}"
|
|
|
|
|
cargo run -p lesavka_client --bin lesavka-sync-probe -- \
|
|
|
|
|
--server "${LESAVKA_SERVER_ADDR}" \
|
|
|
|
|
--duration-seconds "${PROBE_DURATION_SECONDS}"
|
|
|
|
|
)
|
|
|
|
|
fi
|
2026-04-24 14:49:57 -03:00
|
|
|
|
|
|
|
|
echo "==> waiting for browser recording upload"
|
2026-05-01 02:05:07 -03:00
|
|
|
deadline_upload=$(( $(date +%s) + PROBE_DURATION_SECONDS + 60 ))
|
2026-04-24 14:49:57 -03:00
|
|
|
while true; do
|
|
|
|
|
status_json=$(ssh ${SSH_OPTS} "${TETHYS_HOST}" "test -f '${REMOTE_STATUS}' && cat '${REMOTE_STATUS}'" || true)
|
|
|
|
|
if [[ -n "${status_json}" ]]; then
|
|
|
|
|
if STATUS_JSON="${status_json}" python3 -c 'import json, os, sys; status = json.loads(os.environ["STATUS_JSON"]); sys.exit(0 if status.get("uploaded") else 1)'
|
|
|
|
|
then
|
|
|
|
|
echo "==> browser recording uploaded"
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
fi
|
2026-05-01 02:05:07 -03:00
|
|
|
if (( $(date +%s) >= deadline_upload )); then
|
2026-04-24 14:49:57 -03:00
|
|
|
echo "browser recording was not uploaded before timeout" >&2
|
|
|
|
|
[[ -n "${status_json:-}" ]] && echo "last status: ${status_json}" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
sleep 1
|
|
|
|
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
echo "==> fetching capture back to ${LOCAL_CAPTURE}"
|
|
|
|
|
scp ${SSH_OPTS} "${TETHYS_HOST}:${REMOTE_CAPTURE}" "${LOCAL_CAPTURE}"
|
|
|
|
|
|
|
|
|
|
echo "==> analyzing browser capture"
|
2026-05-01 02:05:07 -03:00
|
|
|
analyze_args=(--report-dir "${LOCAL_REPORT_DIR}")
|
|
|
|
|
if [[ -n "${SYNC_ANALYZE_EVENT_WIDTH_CODES}" ]]; then
|
|
|
|
|
analyze_args+=(--event-width-codes "${SYNC_ANALYZE_EVENT_WIDTH_CODES}")
|
|
|
|
|
fi
|
|
|
|
|
analyze_args+=("${LOCAL_CAPTURE}")
|
2026-04-24 14:49:57 -03:00
|
|
|
(
|
|
|
|
|
cd "${REPO_ROOT}"
|
2026-05-01 01:26:24 -03:00
|
|
|
cargo run -p lesavka_client --bin lesavka-sync-analyze -- \
|
2026-05-01 02:05:07 -03:00
|
|
|
"${analyze_args[@]}"
|
2026-04-24 14:49:57 -03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
echo "==> done"
|
|
|
|
|
echo "capture: ${LOCAL_CAPTURE}"
|
2026-05-01 01:26:24 -03:00
|
|
|
echo "report_dir: ${LOCAL_REPORT_DIR}"
|