Punch-list items 19–25 — covers six of the seven services + the engine controller. TeamsLauncher fallback chain (item 21) is deferred: it depends on Process.Start in ways that don't unit-test cleanly without a process-launch seam that the May 2026 codebase doesn't have yet. Service seams added for testability (each marked internal + a matching InternalsVisibleTo-equivalent grant via the existing TeamsISO.App.Tests visibility): * NotesService.DirectoryOverride — redirect %LOCALAPPDATA%\TeamsISO\Notes * WindowStateStore.PathOverride — redirect window.json * UpdateChecker.StateDirectoryOverride — redirect both the 24h cooldown stamp and the no-update-check.flag * UpdateChecker.TryParseSemVer — visibility bumped to internal * OscBridge.DispatchAsync — visibility bumped to internal so tests can drive route dispatch without spinning up the UDP receive loop New test files (App.Tests): * Services/NotesServiceTests.cs (6 cases) — header-once, timestamp format, multi-append, whitespace trim + reject, today-path shape. * Services/UpdateCheckerTests.cs (7 cases) — TryParseSemVer Theory across the v?X.Y.Z(.N)(-suffix) inputs the real release stream produces, semver ordering pin, CheckIfDueAsync short-circuit on recent stamps (the throttle never fires HTTP — deterministic offline), LaunchCheckEnabled round-trip via the opt-out flag. * Services/PresetApplierTests.cs (6 cases) — the four enable/disable state transitions, case-insensitive display-name join, partial meeting (preset names participants not present), live participants unnamed by the preset stay untouched. * Services/PresetStoreCollection.cs — xUnit collection so any test class that mutates OperatorPresetStore.PathOverride serializes with siblings that do the same. OperatorPresetStoreTests now joins the collection (the class comment claimed it didn't need one because file paths were per-test-unique — true, but PathOverride is shared static state, which is why the new PresetApplierTests was clobbering its result on first run). * Services/WindowStateStoreTests.cs (6 cases) — JSON round-trip through the Snapshot record + all the bail paths (no file, too small, too large, fully off-screen, garbage JSON). Full Window property write coverage is deferred to branch 11 (needs STA). * Services/OscBridgeDispatchTests.cs (5 cases) — /teamsiso/refresh- discovery + unknown-address + /teamsiso/notes + clean bail when the toggle/preset paths can't reach a dispatcher. New test cases (Engine.Tests): * Controller/IsoControllerTests.cs gains three cases — SetRecording_TogglesEnabledAndStoresDirectory, AddRecordingMarker_NoOpsCleanly_WhenNoActiveRecorders, RefreshDiscovery_SetsRefreshFlagOnDiscoveryService. Tests: 56 → 128 in App.Tests; 103 → 106 in Engine.Tests. Total green: 234. Build clean (0 warnings, 0 errors). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| docs | ||
| installer | ||
| src | ||
| .editorconfig | ||
| .gitignore | ||
| build-and-test.ps1 | ||
| CHANGELOG.md | ||
| commit-and-push.ps1 | ||
| coverlet.runsettings | ||
| DESIGN.md | ||
| Directory.Build.props | ||
| NEXT_STEPS.md | ||
| PRODUCT.md | ||
| README.md | ||
| TeamsISO.Linux.slnf | ||
| TeamsISO.sln | ||
| TeamsISO.Windows.slnf | ||
TeamsISO
Per-Participant NDI ISO Controller for Microsoft Teams.
TeamsISO sits between Microsoft Teams' raw NDI broadcast output and a live-production environment. It receives each participant's NDI stream, normalizes framerate / resolution / aspect / audio per a configured target, and re-emits clean, individually-addressable NDI sources for ingestion into a switcher (vMix, OBS, Ross, hardware capture).
What it does
- Discovers participants as Teams broadcasts each one over NDI, surfacing the operator-friendly display name (handles current "MS Teams - Name" format and the legacy "(Teams) Name" format).
- Normalizes feeds to a consistent framerate, resolution, aspect mode, and audio routing — so the downstream switcher gets predictable inputs regardless of what each participant's webcam is doing.
- Routes per-participant as separate NDI sources with a configurable
output-name template (
TEAMSISO_{name},{guid},{machine},{timestamp}tokens). - Records each ISO to disk simultaneously — raw BGRA + sidecar manifest.json
- ffmpeg convert.cmd — so post-production gets a clean per-guest archive.
- Embeds Teams orchestration: launch and stop Teams from the rail, hide Teams' UI windows during a show, drive in-call controls (mute, camera, share, leave, raise hand) via UIAutomation.
- Operator presets save the current per-participant ISO assignment and custom output names, applicable on next launch automatically.
- Live preview thumbnails per participant in the participants table, plus pop-out floating preview windows (right-click → Open preview…) for multi-monitor monitoring.
- External control surface — REST + WebSocket on
127.0.0.1:9755and OSC on UDP127.0.0.1:9000for Bitfocus Companion / Stream Deck / TouchOSC integration. Self-contained HTML control panel at/uifor phone-as-controller. - Crash diagnostics wired to a rolling daily Serilog file sink under
%LOCALAPPDATA%\TeamsISO\Logs\. - Update check against
forge.wilddragon.net's release API — manual or silent on launch (throttled to 24h). - Diagnostic bundle export zips logs + config + presets for bug reports.
Status
Pre-1.0. The May 2026 batch is feature-complete; v1.0 cut is gated on
code-signing the MSI and a smoke pass against a real Teams meeting.
See CHANGELOG.md for the [Unreleased] entry.
The May 2026 ground-up redesign — the v2 "Studio Terminal" shell — has
landed on the WPF host (src/TeamsISO.App/). A WinUI 3 replatform was
explored in early May 2026 and abandoned (activation blockers + redundant
work given the redesign is purely XAML / view-layer); the brief lives at
docs/shapes/2026-05-13-teamsiso-v2-studio-terminal.md, and the
abandoned migration plan + bootstrap probe are archived under
docs/archive/.
Build
Requires .NET 8 SDK on Windows. WPF is the only host:
src/TeamsISO.App— WPF,net8.0-windows, the shipping build
Build from the solution filter:
dotnet restore TeamsISO.Windows.slnf
dotnet build TeamsISO.Windows.slnf -c Release
dotnet test TeamsISO.Windows.slnf --filter "Category!=ndi&requires!=ndi"
The shipped helper scripts in the repo root automate this:
pwsh -File .\build-and-test.ps1
pwsh -File .\commit-and-push.ps1
Documentation
- Control surface API — REST + WebSocket + OSC reference with curl recipes and a Companion config example.
- Releasing — tag-push workflow, MSI signing path.
- Architecture spec — design overview.
- Embedded Teams orchestration spec — Phase E roadmap.
- Redesign brief + design system — token-level spec for the v2 "Studio Terminal" redesign.
- v2 shape brief — approved aesthetic + IA for the May 2026 WPF rebuild.
Keyboard shortcuts
| Key | Action |
|---|---|
F1 |
Open help / cheat sheet |
Ctrl + K |
Open the command palette (also Ctrl + P) |
Ctrl + T |
Toggle theme (dark ↔ light) |
Ctrl + M |
Drop a timestamped marker into every active recording |
Ctrl + Shift + S |
Stop every running ISO (emergency) |
Ctrl + R |
Refresh NDI discovery (rebuild finder) |
1–9 / NumPad 1–9 |
Toggle the Nth visible participant's ISO |
File locations
| Path | Contents |
|---|---|
%APPDATA%\TeamsISO\config.json |
Engine settings (framerate, NDI groups, etc.) |
%LOCALAPPDATA%\TeamsISO\presets.json |
Saved operator presets + auto-apply preference |
%LOCALAPPDATA%\TeamsISO\Logs\ |
Rolling daily diagnostic logs |
%LOCALAPPDATA%\TeamsISO\Notes\ |
Per-day show-notes markdown files |
%USERPROFILE%\Videos\TeamsISO\<date>\ |
Default recording output |
%APPDATA%\NDI\ndi-config.v1.json |
NDI Access Manager group routing |
License
Proprietary, © Wild Dragon LLC 2026.