#!/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