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