Auto-record when Teams joins a meeting
Some checks failed
CI / build-and-test (push) Failing after 26s

New AutoRecordOnCall preference (DISPLAY tab). When checked, recording auto-flips ON the moment Teams transitions into a call (UIA Leave button appears in tree), and auto-flips OFF when the call ends.

Completes the unattended-show story: with Launch + AutoHide + AutoRecord all ticked, the operator launches TeamsISO and walks away — Teams runs invisibly, recording begins/ends with the meeting, ISOs route, all done. Toast surfaces each transition so they know what's happening if they glance at the screen.

Implementation: transition detection lives in the existing UIA-probe code in OnStatsTick. previousInCall != inCall gate prevents the auto-toggle from re-firing on every poll. Direct call to _controller.SetRecording + Settings.RecordIsosToDisk = ... so the existing recording infrastructure handles the rest. Toast for visibility, swallow-on-error so a recording config issue can't break the IN-CALL pill update path.
This commit is contained in:
Zac Gaetano 2026-05-10 21:10:30 -04:00
parent 1d5d055b68
commit aa07ad9f08
4 changed files with 59 additions and 2 deletions

View file

@ -1338,6 +1338,11 @@
Margin="0,8,0,0"
ToolTip="When checked, Teams' windows are hidden as soon as they materialize after launch. Use the eye-icon button in the rail to restore them when needed. Drives Teams via the IN-CALL bar (mute / camera / share / leave / marker) and the participants DataGrid for ISO routing."/>
<CheckBox Content="Auto-record when Teams joins a meeting"
IsChecked="{Binding Settings.AutoRecordOnCall}"
Margin="0,8,0,0"
ToolTip="When checked, recording auto-starts the moment Teams transitions into a call (IN CALL pill goes cyan) and auto-stops when the call ends. Removes the manual Record toggle step from unattended-show workflows."/>
<Separator Margin="0,16,0,8"/>
<CheckBox Content="Record ISOs to disk"

View file

@ -45,7 +45,12 @@ public static class UIPreferences
// background and its windows are auto-hidden as soon as they materialize.
// All control happens via the IN-CALL bar + participants DataGrid.
bool LaunchTeamsOnStartup = false,
bool AutoHideTeamsWindows = false);
bool AutoHideTeamsWindows = false,
// Auto-record on meeting start: when Teams transitions into a call
// (UIA Leave button appears), TeamsISO flips global recording on;
// when the call ends, recording stops. Pairs naturally with the
// headless workflow — operator never touches the recording toggle.
bool AutoRecordOnCall = false);
public static Prefs Load()
{

View file

@ -36,6 +36,7 @@ public sealed class GlobalSettingsViewModel : ObservableObject
private bool _controlSurfaceLanReachable;
private bool _launchTeamsOnStartup;
private bool _autoHideTeamsWindows;
private bool _autoRecordOnCall;
public GlobalSettingsViewModel(IIsoController controller, ToastViewModel? toast = null)
{
@ -63,6 +64,7 @@ public sealed class GlobalSettingsViewModel : ObservableObject
_controlSurfaceLanReachable = uiPrefs.ControlSurfaceLanReachable;
_launchTeamsOnStartup = uiPrefs.LaunchTeamsOnStartup;
_autoHideTeamsWindows = uiPrefs.AutoHideTeamsWindows;
_autoRecordOnCall = uiPrefs.AutoRecordOnCall;
// Bring the auto-apply flag in from the presets store so the checkbox
// reflects the user's prior choice when the settings panel opens.
@ -255,7 +257,8 @@ public sealed class GlobalSettingsViewModel : ObservableObject
MinimizeToTray: _minimizeToTray,
ControlSurfaceLanReachable: _controlSurfaceLanReachable,
LaunchTeamsOnStartup: _launchTeamsOnStartup,
AutoHideTeamsWindows: _autoHideTeamsWindows));
AutoHideTeamsWindows: _autoHideTeamsWindows,
AutoRecordOnCall: _autoRecordOnCall));
/// <summary>
/// Auto-launch the Microsoft Teams desktop client when TeamsISO starts.
@ -289,6 +292,21 @@ public sealed class GlobalSettingsViewModel : ObservableObject
}
}
/// <summary>
/// When Teams transitions into a call, automatically enable recording;
/// when the call ends, stop. Removes the manual record-toggle step from
/// the headless workflow. Recording state changes are surfaced via the
/// existing toast + footer badge so the operator knows what's happening.
/// </summary>
public bool AutoRecordOnCall
{
get => _autoRecordOnCall;
set
{
if (SetField(ref _autoRecordOnCall, value)) PersistUiPrefs();
}
}
/// <summary>
/// Record each newly-enabled ISO's normalized output to disk under
/// <see cref="RecordingDirectory"/>. Already-running ISOs are not retroactively

View file

@ -693,10 +693,39 @@ public sealed class MainViewModel : ObservableObject, IDisposable
var title = inCall ? ExtractMeetingTitle(TeamsLauncher.GetActiveWindowTitle()) : null;
_dispatcher.InvokeAsync(() =>
{
var previousInCall = IsTeamsInCall;
IsTeamsInCall = inCall;
TeamsMeetingState = inCall
? (string.IsNullOrEmpty(title) ? "IN CALL" : $"IN CALL · {title}")
: "READY";
// Auto-record on meeting transitions. False→True
// turns recording on; True→False turns it off.
// Guarded against repeating on every poll by
// gating on the actual transition.
if (Settings.AutoRecordOnCall && previousInCall != inCall)
{
if (inCall && !_controller.RecordingEnabled)
{
try
{
_controller.SetRecording(true, Settings.RecordingDirectory);
Settings.RecordIsosToDisk = true;
Toast.Show("Auto-record: meeting started — recording ON");
}
catch { /* defensive — recording shouldn't block other state */ }
}
else if (!inCall && _controller.RecordingEnabled)
{
try
{
_controller.SetRecording(false, null);
Settings.RecordIsosToDisk = false;
Toast.Show("Auto-record: meeting ended — recording OFF");
}
catch { /* defensive */ }
}
}
});
}
catch { /* UIA flakiness shouldn't crash the stats tick */ }