134 lines
5.2 KiB
Bash
Executable File
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
|