ATEM: restore file saved 2026-10-13 15:54 (camera 2 tint 15); shutdown budget capped at launchd's 60 s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -142,7 +142,7 @@ This folder is the source of truth. Edit `config.json` here, then copy the folde
|
||||
scp status_panel.py coremidi.py netgear.py make_page.py push_page.py nav_keys.py nav_templates.json config.json install.sh atem_watch.js atem_state.js atem-reference.xml checklist-ingest:status-panel-install/
|
||||
```
|
||||
|
||||
Then run the installer there. It checks the config with the new code first (a bad config never gets as far as the watchdog), keeps the code that was running in `prev/`, backs up the old config, writes the LaunchAgent with the exit timeout this config needs (the longest fix plus its verify time, so a restart never cuts a fix in half) and restarts the watchdog:
|
||||
Then run the installer there. It checks the config with the new code first (a bad config never gets as far as the watchdog), keeps the code that was running in `prev/`, backs up the old config, writes the LaunchAgent with the exit timeout this config needs (the longest fix plus its verify time, so a restart never cuts a fix in half; launchd honours at most 60 s of it) and restarts the watchdog:
|
||||
|
||||
```bash
|
||||
ssh checklist-ingest 'zsh ~/status-panel-install/install.sh'
|
||||
|
||||
+11
-11
@@ -2,8 +2,8 @@
|
||||
<Profile majorVersion="2" minorVersion="1" product="ATEM 4 M/E Constellation 4K Plus">
|
||||
<MixEffectBlocks>
|
||||
<MixEffectBlock index="0">
|
||||
<Program input="18"/>
|
||||
<Preview input="3"/>
|
||||
<Program input="17"/>
|
||||
<Preview input="18"/>
|
||||
<NextTransition selection="Background" nextSelection="Background"/>
|
||||
<TransitionStyle style="Mix" nextStyle="Mix" previewTransition="False" transitionPosition="0">
|
||||
<MixParameters rate="7"/>
|
||||
@@ -13,7 +13,7 @@
|
||||
<DVEParameters rate="30" logoRate="30" reverseDirection="False" flipFlop="False" effect="PushRight" fillSource="3010" keySource="3011" enableKey="True" preMultipliedKey="True" clip="50" gain="70" invertKey="False"/>
|
||||
</TransitionStyle>
|
||||
<Keys>
|
||||
<Key index="0" type="Luma" inputFill="13" inputCut="12" onAir="False" masked="False" maskTop="9" maskBottom="-9" maskLeft="-16" maskRight="16">
|
||||
<Key index="0" type="Luma" inputFill="13" inputCut="12" onAir="True" masked="False" maskTop="9" maskBottom="-9" maskLeft="-16" maskRight="16">
|
||||
<LumaParameters preMultiplied="True" clip="50" gain="70" inverse="False"/>
|
||||
<AdvancedChromaParameters foregroundLevel="0" backgroundLevel="0" keyEdge="50" spillSuppress="0" flareSuppress="0" brightness="0" contrast="0" saturation="100" red="0" green="0" blue="0" cursorXPosition="-15.41" cursorYPosition="8.41" cursorSize="6.25" sampledY="0.6748" sampledCb="0.1621" sampledCr="0.1015"/>
|
||||
<PatternParameters style="DiamondIris" inverse="False" size="50" symmetry="50" softness="0" xPosition="0.5" yPosition="0.5"/>
|
||||
@@ -57,7 +57,7 @@
|
||||
<DVEParameters rate="30" logoRate="30" reverseDirection="False" flipFlop="False" effect="PushRight" fillSource="3010" keySource="3011" enableKey="True" preMultipliedKey="True" clip="50" gain="70" invertKey="False"/>
|
||||
</TransitionStyle>
|
||||
<Keys>
|
||||
<Key index="0" type="Luma" inputFill="13" inputCut="12" onAir="False" masked="False" maskTop="9" maskBottom="-9" maskLeft="-16" maskRight="16">
|
||||
<Key index="0" type="Luma" inputFill="13" inputCut="12" onAir="True" masked="False" maskTop="9" maskBottom="-9" maskLeft="-16" maskRight="16">
|
||||
<LumaParameters preMultiplied="True" clip="50" gain="70" inverse="False"/>
|
||||
<AdvancedChromaParameters foregroundLevel="0" backgroundLevel="0" keyEdge="50" spillSuppress="0" flareSuppress="0" brightness="0" contrast="0" saturation="100" red="0" green="0" blue="0" cursorXPosition="-15.41" cursorYPosition="8.41" cursorSize="6.25" sampledY="0.6748" sampledCb="0.1621" sampledCr="0.1015"/>
|
||||
<PatternParameters style="DiamondIris" inverse="False" size="50" symmetry="50" softness="0" xPosition="0.5" yPosition="0.5"/>
|
||||
@@ -90,7 +90,7 @@
|
||||
<ForceTally tally=""/>
|
||||
</MixEffectBlock>
|
||||
<MixEffectBlock index="2">
|
||||
<Program input="1"/>
|
||||
<Program input="16"/>
|
||||
<Preview input="3"/>
|
||||
<NextTransition selection="Background" nextSelection="Background"/>
|
||||
<TransitionStyle style="Mix" nextStyle="Mix" previewTransition="False" transitionPosition="0">
|
||||
@@ -134,8 +134,8 @@
|
||||
<ForceTally tally=""/>
|
||||
</MixEffectBlock>
|
||||
<MixEffectBlock index="3">
|
||||
<Program input="14"/>
|
||||
<Preview input="6"/>
|
||||
<Program input="17"/>
|
||||
<Preview input="17"/>
|
||||
<NextTransition selection="Background" nextSelection="Background"/>
|
||||
<TransitionStyle style="Mix" nextStyle="Mix" previewTransition="False" transitionPosition="0">
|
||||
<MixParameters rate="7"/>
|
||||
@@ -4459,9 +4459,9 @@
|
||||
</SuperSources>
|
||||
<MediaPool>
|
||||
<Stills>
|
||||
<Still index="0" name="Welcome 4" path="ATEM Media Pool/Welcome 4.png"/>
|
||||
<Still index="1" name="2501_ThankYou 5" path="ATEM Media Pool/2501_ThankYou 5.png"/>
|
||||
<Still index="2" name="2408_HourOfPrayer 5" path="ATEM Media Pool/2408_HourOfPrayer 5.png"/>
|
||||
<Still index="0" name="Welcome 5" path="ATEM Media Pool/Welcome 5.png"/>
|
||||
<Still index="1" name="2501_ThankYou 6" path="ATEM Media Pool/2501_ThankYou 6.png"/>
|
||||
<Still index="2" name="2408_HourOfPrayer 6" path="ATEM Media Pool/2408_HourOfPrayer 6.png"/>
|
||||
</Stills>
|
||||
</MediaPool>
|
||||
<CameraControl>
|
||||
@@ -4481,7 +4481,7 @@
|
||||
<Parameter device="1" category="ColorCorrection" parameter="ColorAdjust" hue="0" saturation="1"/>
|
||||
<Parameter device="2" category="Lens" parameter="ApertureFstop" value="5"/>
|
||||
<Parameter device="2" category="Lens" parameter="ApertureNormalised" value="0"/>
|
||||
<Parameter device="2" category="Video" parameter="ManualWhiteBalance" temperature="4500" tint="0"/>
|
||||
<Parameter device="2" category="Video" parameter="ManualWhiteBalance" temperature="4500" tint="15"/>
|
||||
<Parameter device="2" category="Video" parameter="Exposure" value="16667"/>
|
||||
<Parameter device="2" category="Video" parameter="DetailLevel" value="1"/>
|
||||
<Parameter device="2" category="Video" parameter="SensorGainInDecibels" value="2"/>
|
||||
|
||||
+9
-4
@@ -2501,9 +2501,13 @@ def check_host(cfg, host, where):
|
||||
raise ValueError("%s: host %s has no ssh address" % (where, host))
|
||||
|
||||
|
||||
LAUNCHD_EXIT_CAP = 60 # launchd on macOS 26 applies at most 60 s of ExitTimeOut, whatever the plist says
|
||||
|
||||
|
||||
def exit_timeout(cfg):
|
||||
"""How long launchd must let the watchdog finish a running fix before killing it: the longest
|
||||
fix plus its verify time plus a margin, never under a minute (install.sh writes it into the plist)."""
|
||||
"""How long launchd should let the watchdog finish a running fix before killing it: the longest
|
||||
fix plus its verify time plus a margin, never under a minute (install.sh writes it into the plist;
|
||||
launchd caps what it honours at LAUNCHD_EXIT_CAP, so the shutdown budget is capped too)."""
|
||||
longest = 0
|
||||
for fix in cfg.get("fixes", {}).values():
|
||||
longest = max(longest, fix.get("timeout", 60) + (fix.get("verify_seconds", 20)
|
||||
@@ -2767,8 +2771,9 @@ def run(cfg, data_dir, dry_run=False, stop=None, config_sha=None):
|
||||
% (prefix, alert_after))
|
||||
alerted = True
|
||||
stop.wait(0.25)
|
||||
# A fix that is running finishes before the process goes (launchd waits exit_timeout for us).
|
||||
budget = time.time() + max(5, exit_timeout(cfg) - 5)
|
||||
# A fix that is running finishes before the process goes (launchd waits up to the exit timeout,
|
||||
# capped at LAUNCHD_EXIT_CAP, for us).
|
||||
budget = time.time() + max(5, min(exit_timeout(cfg), LAUNCHD_EXIT_CAP) - 5)
|
||||
for t in engine.fix_threads:
|
||||
t.join(timeout=max(0.1, budget - time.time()))
|
||||
if any(t.is_alive() for t in engine.fix_threads):
|
||||
|
||||
Reference in New Issue
Block a user