fix(server): map eye links from distinct capture nodes

This commit is contained in:
Brad Stein 2026-04-25 05:57:29 -03:00
parent 76a6df4f60
commit 97abb938c5
6 changed files with 48 additions and 38 deletions

6
Cargo.lock generated
View File

@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "lesavka_client"
version = "0.13.15"
version = "0.13.16"
dependencies = [
"anyhow",
"async-stream",
@ -1676,7 +1676,7 @@ dependencies = [
[[package]]
name = "lesavka_common"
version = "0.13.15"
version = "0.13.16"
dependencies = [
"anyhow",
"base64",
@ -1688,7 +1688,7 @@ dependencies = [
[[package]]
name = "lesavka_server"
version = "0.13.15"
version = "0.13.16"
dependencies = [
"anyhow",
"base64",

View File

@ -4,7 +4,7 @@ path = "src/main.rs"
[package]
name = "lesavka_client"
version = "0.13.15"
version = "0.13.16"
edition = "2024"
[dependencies]

View File

@ -1,6 +1,6 @@
[package]
name = "lesavka_common"
version = "0.13.15"
version = "0.13.16"
edition = "2024"
build = "build.rs"

View File

@ -320,49 +320,43 @@ if systemctl list-unit-files | grep -q '^relay.service'; then
sleep 2
fi
# probe v4l2 devices for GC311s (07ca:3311)
mapfile -t GC_VIDEOS < <(
sudo v4l2-ctl --list-devices 2>/dev/null |
awk '/Live Gamer MINI/{getline; print $1}'
mapfile -t GC_CAPTURE_PAIRS < <(
for dev in /dev/video*; do
props=$(sudo udevadm info -q property -n "$dev" 2>/dev/null || true)
index=$(cat "/sys/class/video4linux/$(basename "$dev")/index" 2>/dev/null || true)
if echo "$props" | grep -q 'ID_VENDOR_ID=07ca' \
&& echo "$props" | grep -q 'ID_MODEL_ID=3311' \
&& [ "$index" = "0" ]; then
tag=$(printf '%s\n' "$props" | awk -F= '/^ID_PATH_TAG=/{print $2}')
if [ -n "$tag" ]; then
printf '%s %s\n' "$tag" "$dev"
fi
fi
done | sort -u
)
# fallback via udev if v4l2-ctl output is empty/partial
if [ "${#GC_VIDEOS[@]}" -ne 2 ]; then
mapfile -t GC_VIDEOS < <(
for dev in /dev/video*; do
props=$(sudo udevadm info -q property -n "$dev" 2>/dev/null || true)
if echo "$props" | grep -q 'ID_VENDOR_ID=07ca' && echo "$props" | grep -q 'ID_MODEL_ID=3311'; then
echo "$dev"
fi
done | sort -u
)
fi
if [ "${#GC_VIDEOS[@]}" -ne 2 ]; then
if [ "${#GC_CAPTURE_PAIRS[@]}" -ne 2 ]; then
echo "⚠️ GC311 capture cards not fully present; skipping udev eye-link refresh." >&2
if [ "${#GC_VIDEOS[@]}" -eq 0 ]; then
if [ "${#GC_CAPTURE_PAIRS[@]}" -eq 0 ]; then
echo " Detected: none" >&2
else
printf ' Detected: %s\n' "${GC_VIDEOS[@]}" >&2
printf ' Detected: %s\n' "${GC_CAPTURE_PAIRS[@]}" >&2
fi
echo " The server install will continue, and existing /dev/lesavka_* links stay untouched." >&2
else
mapfile -t TAGS < <(
for v in "${GC_VIDEOS[@]}"; do
sudo udevadm info -q property -n "$v" |
awk -F= '/^ID_PATH_TAG=/{print $2}'
done
)
LEFT_TAG=${GC_CAPTURE_PAIRS[0]%% *}
LEFT_DEV=${GC_CAPTURE_PAIRS[0]#* }
RIGHT_TAG=${GC_CAPTURE_PAIRS[1]%% *}
RIGHT_DEV=${GC_CAPTURE_PAIRS[1]#* }
if [ -z "${TAGS[0]:-}" ] || [ -z "${TAGS[1]:-}" ]; then
echo "⚠️ GC311 cards were detected, but ID_PATH_TAG lookup was incomplete." >&2
if [ -z "$LEFT_TAG" ] || [ -z "$RIGHT_TAG" ] || [ "$LEFT_TAG" = "$RIGHT_TAG" ]; then
echo "⚠️ GC311 cards were detected, but the capture path tags are incomplete or duplicated." >&2
printf ' Left candidate: %s\n' "${GC_CAPTURE_PAIRS[0]:-missing}" >&2
printf ' Right candidate: %s\n' "${GC_CAPTURE_PAIRS[1]:-missing}" >&2
echo " Skipping udev eye-link refresh and preserving any existing /dev/lesavka_* links." >&2
else
printf ' ↪ Left card: %s (%s)\n' "${GC_VIDEOS[0]}" "${TAGS[0]}"
printf ' ↪ Right card: %s (%s)\n' "${GC_VIDEOS[1]}" "${TAGS[1]}"
LEFT_TAG=${TAGS[0]}
RIGHT_TAG=${TAGS[1]}
printf ' ↪ Left card: %s (%s)\n' "$LEFT_DEV" "$LEFT_TAG"
printf ' ↪ Right card: %s (%s)\n' "$RIGHT_DEV" "$RIGHT_TAG"
sudo tee /etc/udev/rules.d/85-gc311.rules >/dev/null <<EOF
# auto-generated by lesavka/scripts/daemon/install-server.sh - DO NOT EDIT

View File

@ -10,7 +10,7 @@ bench = false
[package]
name = "lesavka_server"
version = "0.13.15"
version = "0.13.16"
edition = "2024"
autobins = false

View File

@ -55,3 +55,19 @@ fn server_install_grants_operator_audio_group_access() {
"install script should target the sudo-invoking operator instead of a hard-coded account"
);
}
#[test]
fn server_install_builds_eye_links_from_capture_nodes_only() {
assert!(
SERVER_INSTALL.contains("/sys/class/video4linux/$(basename \"$dev\")/index"),
"install script should inspect the kernel video index so it only links real capture nodes"
);
assert!(
SERVER_INSTALL.contains("GC_CAPTURE_PAIRS"),
"install script should assemble eye-link candidates from udev-tagged capture devices"
);
assert!(
SERVER_INSTALL.contains("[ \"$LEFT_TAG\" = \"$RIGHT_TAG\" ]"),
"install script should refuse duplicated path tags instead of assigning both eyes to one card"
);
}