SmartestHome/hosts/thin-client/configs/idle-gallery/idle-gallery.sh

119 lines
4.9 KiB
Bash

#!/bin/sh
# Idle-timeout photo slideshow. Installed to /usr/local/bin/idle-gallery, invoked
# directly by swayidle's `timeout`/`resume` commands in configs/sway/config — not a
# sway `exec_always`, so the PID-lock-guard idiom in fullscreen-watcher.sh and
# gesture-control.sh (needed there to survive repeated `swaymsg reload`) does not
# apply here; swayidle only ever calls this on its own timeout/resume edges, so plain
# idempotent checks (`mountpoint -q`, `pgrep -f`) are enough.
#
# Two-tier fallback, both ways: no gallery credentials configured, an unreachable
# share, or an empty share must all fall back to the plain output-blank that this
# script's caller (configs/sway/config) previously did unconditionally — a half-lit
# fullscreen error is worse than blanking, and this is the ONLY thing standing between
# an idle kiosk and screen burn-in, so it must never just silently do nothing.
set -eu
CONFIG_ENV=/etc/thinclient-agent/config.env
CREDENTIALS=/etc/thinclient-agent/gallery-credentials
MOUNTPOINT=/mnt/gallery
PLAYLIST="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/idle-gallery-playlist.m3u"
# Distinguishes this mpv instance from one a person (or thinclient-agent) launched
# deliberately, so `stop` can pkill exactly it and nothing else — see mpv.conf's own
# --input-ipc-server for why a marker in argv, not a socket, is the simpler match here.
MARKER="--playlist=$PLAYLIST"
DISPLAY_SECONDS=10
blank() {
swaymsg "output * power off" >/dev/null 2>&1 || true
}
# Weather/clock overlay (configs/eww/eww.yuck's weather-overlay window) — this
# script is the sole owner of its visibility, the same way fullscreen-watcher.sh
# owns the now-playing widget's. Best-effort throughout: an image built without
# eww (0800-eww-widget.hook.chroot's documented placeholder path) or a session
# where the daemon has not started yet must never turn a slideshow failure into a
# script failure, so every step here is allowed to silently no-op.
show_weather_overlay() {
command -v eww >/dev/null 2>&1 || return 0
eww daemon >/dev/null 2>&1 || true
eww open weather-overlay >/dev/null 2>&1 || true
}
hide_weather_overlay() {
command -v eww >/dev/null 2>&1 || return 0
eww close weather-overlay >/dev/null 2>&1 || true
}
stop_slideshow() {
pkill -u "$(id -u)" -f "mpv .*${MARKER}" 2>/dev/null || true
hide_weather_overlay
}
if [ "${1:-start}" = "stop" ]; then
stop_slideshow
exit 0
fi
# Already running (e.g. a second timeout fired before the first resume) — nothing to do.
if pgrep -u "$(id -u)" -f "mpv .*${MARKER}" >/dev/null 2>&1; then
exit 0
fi
if [ ! -r "$CREDENTIALS" ]; then
echo "idle-gallery: $CREDENTIALS not set up yet — see hosts/thin-client/README.md. Blanking instead." >&2
blank
exit 0
fi
# shellcheck disable=SC1090
. "$CONFIG_ENV" 2>/dev/null || true
if [ -z "${GALLERY_SMB_HOST:-}" ]; then
echo "idle-gallery: GALLERY_SMB_HOST is unset in $CONFIG_ENV. Blanking instead." >&2
blank
exit 0
fi
if ! mountpoint -q "$MOUNTPOINT" 2>/dev/null; then
sudo mkdir -p "$MOUNTPOINT"
# soft + a short connect timeout: a share that is merely unreachable right now (the
# container host is off, the network hiccuped) must fail in a few seconds, not hang
# the whole idle-transition — same "never block on a remote service" rule Phase
# 11.10 already applies to thinclient-agent's own MQTT connection.
if ! sudo mount -t cifs "//${GALLERY_SMB_HOST}/gallery" "$MOUNTPOINT" \
-o "credentials=${CREDENTIALS},ro,vers=3.0,soft,retry=0,echo_interval=5,uid=$(id -u),gid=$(id -g)" \
2>/tmp/idle-gallery-mount.err
then
echo "idle-gallery: mount failed — $(cat /tmp/idle-gallery-mount.err 2>/dev/null). Blanking instead." >&2
blank
exit 0
fi
fi
find "$MOUNTPOINT" -type f \( \
-iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' \
\) 2>/dev/null | shuf > "$PLAYLIST"
if [ ! -s "$PLAYLIST" ]; then
echo "idle-gallery: $MOUNTPOINT has no images. Blanking instead." >&2
rm -f "$PLAYLIST"
blank
exit 0
fi
# Backgrounded, not exec'd: swayidle expects its timeout command to return, not to
# become the slideshow for as long as it runs. --fs, not --fullscreen: sway's own
# `for_window [app_id="mpv"] fullscreen enable` already applies, this just tells mpv
# not to draw window decorations in the instant before that rule lands. app_id stays
# the plain mpv one on purpose — fullscreen-watcher.sh's now-playing widget already
# treats any fullscreen mpv window as "visual content on screen" and hides itself,
# which is exactly the right behaviour here too.
mpv --fs --no-osc --idle=no --image-display-duration="$DISPLAY_SECONDS" \
--loop-playlist=inf --shuffle "--playlist=$PLAYLIST" \
>/tmp/idle-gallery-mpv.log 2>&1 &
disown
# After mpv is actually launched, not before — a failed launch above (this whole
# block is under `set -e`, so mpv itself failing to start would already have exited
# the script) must never leave the overlay showing over a blank/off panel.
show_weather_overlay