SmartestHome/hosts/steam-tv-box/configs/session/media-session

134 lines
5.2 KiB
Bash
Executable File

#!/bin/sh
# Brings up (or takes down) the media half of this box. Installed to
# /usr/local/bin/media-session.
#
# WHAT THIS IS FOR
# ----------------
# The requirement was: boot into Big Picture, and on leaving it have the same media
# functions as the other clients that drive a monitor — Spotify, a general web browser
# with uBlock Origin, a general mp3/mp4 player, and working audio — with those apps
# only launched when Big Picture is actually left. This script is the "launch them"
# half; steam-session and session-watcher are the two things that call it.
#
# WHY LAZY AT ALL
# ---------------
# Not tidiness. A machine that is playing a game is the one machine here where a
# background Firefox and a background Electron/Spotify client cost something visible:
# they hold GPU memory, they wake the CPU on timers, and Spotify in particular keeps an
# audio stream open that shows up as stutter in a title that is already frame-limited.
# Starting them the first time somebody leaves the game means a gaming session pays
# nothing for media features it isn't using.
#
# IDEMPOTENCE
# -----------
# Every launch below is guarded by its own pgrep, and the whole start path holds an
# flock, so:
# - the two triggers firing at once cannot produce two Spotifys;
# - leaving and re-entering Big Picture repeatedly costs one pgrep per app;
# - an app the user closed by hand comes back the next time they leave the game,
# which is what "the media session is up" should mean.
# There is deliberately no "already started" flag file: a flag would go stale the
# moment somebody quit one of the apps, and the per-app check is the honest question.
#
# WHAT LEAVING BIG PICTURE DOES *NOT* DO
# --------------------------------------
# Going back into Steam does not stop any of this. Killing a running Spotify because
# someone launched a game would lose whatever was playing — and people do play music
# over a game on purpose. `media-session stop` exists for when you actually want the
# machine quiet (it is wired to an HA button); nothing calls it automatically.
set -eu
WS_WEB="3:web"
WS_MEDIA="4:media"
WS_MUSIC="5:music"
LOCK_DIR="${XDG_RUNTIME_DIR:-/tmp}"
LOCK_FILE="${LOCK_DIR}/steamtv-media-session.lock"
log() { echo "media-session: $*"; }
# Launch `command` on `workspace` unless something matching `pattern` is already this
# user's. The workspace switch happens before the launch for the same reason
# steamtv_agent.sway_control.launch_app does it that way: sway places a new window on
# whatever workspace is focused when it maps, and there is no reliable per-app `assign`
# rule for two Firefox profiles or for a flatpak's app_id.
launch_unless_running() {
pattern="$1"
workspace="$2"
shift 2
if pgrep -u "$(id -u)" -f "$pattern" >/dev/null 2>&1; then
log "$1 already running"
return 0
fi
log "launching $1 on $workspace"
swaymsg workspace "$workspace" >/dev/null 2>&1 || true
setsid "$@" >/dev/null 2>&1 &
# Give sway a moment to map the window on the workspace we just switched to, before
# the next launch switches away again. Crude, and correct: the alternative is
# subscribing to window events for each app, which is a lot of machinery for a path
# that runs once per session.
sleep 1
}
start() {
focus_after="${1:-}"
launch_unless_running "com.spotify.Client" "$WS_MUSIC" /usr/local/bin/spotify-launch
launch_unless_running "media-player-idle" "$WS_MEDIA" /usr/local/bin/media-player
# web-browser has its own focus-an-existing-window logic (it holds state a relaunch
# would throw away), so the pattern here matches its profile directory rather than
# the bare binary — a second Firefox on a different profile is a different window.
launch_unless_running "firefox.*--profile.*/firefox/web" "$WS_WEB" /usr/local/bin/web-browser
if [ "$focus_after" = "--focus" ]; then
swaymsg workspace "$WS_WEB" >/dev/null 2>&1 || true
fi
log "media session up"
}
stop() {
# pkill by the same patterns the launches are guarded by, so start/stop cannot
# disagree about what counts as "running". SIGTERM only — Spotify and Firefox both
# persist state on exit, and a SIGKILL here is how you get a "Firefox didn't shut
# down properly" dialog on the TV next time.
for pattern in "com.spotify.Client" "media-player-idle" "firefox.*--profile.*/firefox/web"; do
pkill -u "$(id -u)" -f "$pattern" >/dev/null 2>&1 || true
done
log "media session stopped"
}
case "${1:-start}" in
start)
shift 2>/dev/null || true
# flock serialises the two triggers. -n: if another start is already in flight,
# this one has nothing to add — the in-flight one will launch the same apps.
if command -v flock >/dev/null 2>&1; then
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
log "another start is already running; nothing to do"
exit 0
fi
fi
start "${1:-}"
;;
stop)
stop
;;
status)
for pattern in "com.spotify.Client" "media-player-idle" "firefox.*--profile.*/firefox/web"; do
if pgrep -u "$(id -u)" -f "$pattern" >/dev/null 2>&1; then
echo "running: $pattern"
else
echo "stopped: $pattern"
fi
done
;;
*)
echo "usage: media-session [start [--focus] | stop | status]" >&2
exit 2
;;
esac