ProPresenter updates are safe: any installed copy counts as running, the newest version opens

- newest_of picks by CFBundleShortVersionString, not file name (ProPresenter.app sorted after ProPresenter 20.1.app)
- mac_start takes newest_of; auto-setup uses it for programs on the Companion Mac
- WS and SC switched over; NA's ProP restart re-rendered (same pick today)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 14:33:39 -04:00
co-authored by Claude Opus 5.5
parent 5db1d93732
commit 42b325e927
7 changed files with 82 additions and 24 deletions
+21 -3
View File
@@ -64,6 +64,22 @@ def mac_quit(process, wait_seconds, force, stop_children=False):
return s
def newest_app(pattern):
"""APP=... the newest installed copy by its own version number (CFBundleShortVersionString), not by
file name: "ProPresenter.app" sorts after "ProPresenter 20.1.app" by name (2026-10-21, before the
Southcoast/Woonsocket ProPresenter update)."""
return ("APP=$(for a in %s; do [ -d \"$a\" ] && printf '%%s|%%s\\n' \"$(defaults read \"$a/Contents/Info\" "
"CFBundleShortVersionString 2>/dev/null)\" \"$a\"; done | sort -t '|' -k1,1V | tail -1 | cut -d '|' -f2-); "
% str(pattern).strip())
def app_match(pattern, process):
"""A pgrep -f pattern for the program from any copy matching an /Applications glob (never its helpers)."""
import re as _re
parts = [_re.escape(x) for x in str(pattern).split("*")]
return "[^/]*".join(parts).replace("\\ ", " ") + "/Contents/MacOS/" + _re.escape(process).replace("\\ ", " ") + "$"
def mac_open(app, background=False, arg=None):
"""open -a Name / open /path/App.app (+ -g to stay in the background)."""
app = str(app)
@@ -109,8 +125,10 @@ TEMPLATES = {
"words": "Start a Mac program if it is not running",
"os": "mac", "start_only": True,
"params": [P_HOST, ("process", "Process name", "As pgrep sees it (Activity Monitor's process name).", "str", None),
("app", "App to open", "Its name, or the full /Applications/... path.", "str", None)],
"render": lambda p, cfg: "pgrep -xq %s || %s" % (q(p["process"]), mac_open(p["app"])),
("app", "App to open", "Its name, the full /Applications/... path, or $APP with 'newest of' below.", "str", None),
("newest_of", "Newest of", "A pattern like /Applications/ProPresenter*.app: the newest installed version opens.", "str", "")],
"render": lambda p, cfg: ("pgrep -xq %s || { %s%s; }" % (q(p["process"]), newest_app(p["newest_of"]), mac_open("$APP"))
if p.get("newest_of") else "pgrep -xq %s || %s" % (q(p["process"]), mac_open(p["app"]))),
"suggest": lambda p, cfg: "start %s if it is not running" % p["app"],
},
"mac_restart": {
@@ -123,7 +141,7 @@ TEMPLATES = {
("background", "Open it in the background", "No window comes to the front.", "bool", False),
("stop_children", "Also stop everything it started", "Its helper processes (a translation server, say).", "bool", False),
("newest_of", "Newest of", "A pattern like /Applications/ProPresenter*.app: the newest installed version opens.", "str", "")],
"render": lambda p, cfg: (("APP=$(ls -d %s | sort -V | tail -1); " % str(p["newest_of"]).strip()) if p.get("newest_of") else "")
"render": lambda p, cfg: (newest_app(p["newest_of"]) if p.get("newest_of") else "")
+ mac_quit(p["process"], p.get("wait_seconds", 10), p.get("force", True), p.get("stop_children", False))
+ mac_open("$APP" if p.get("newest_of") else p["app"], p.get("background", False)),
"suggest": lambda p, cfg: "restart %s" % (p["app"] if not p.get("newest_of") else "%s (newest installed)" % p["process"]),