Skip to content

Keep Teams setup off the recording path - #202

Open
Lazare-42 wants to merge 5 commits into
output-flac-cloak-browserfrom
fix/v1-onprem-teams-nonblocking-20260702
Open

Lazare-42 wants to merge 5 commits into
output-flac-cloak-browserfrom
fix/v1-onprem-teams-nonblocking-20260702

Conversation

@Lazare-42

Copy link
Copy Markdown
Contributor

Summary

  • run Teams joins through CloakBrowser on the v1 branch
  • remove the Chrome runtime dependency from the bot image
  • keep post-join HTML cleanup and speaker observation non-blocking so a slow or unresponsive meeting page does not abort recording startup

Validation

  • NPM_CONFIG_CACHE=/data/lazrossi/.npm npm run build

Lazare-42 added 4 commits July 2, 2026 19:44
HTML cleanup and speaker observation can hang after a successful join if the Teams page becomes unresponsive. Keep them off the setup critical path so recording can start and RecordingState owns leave decisions.
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 62481015-339a-4b13-bd94-96e6668710cd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR migrates browser launching fully to CloakBrowser, adds external-navigation blocking and click-handling changes for Teams meetings, injects a cleanup stylesheet in Teams HTML cleaning, reworks recording sync/offset timing calculations, and adjusts non-blocking sequencing across several state-machine states.

Changes

Browser Launch & CloakBrowser Migration

Layer / File(s) Summary
Dockerfile dependency install and unconditional CloakBrowser launch
Dockerfile, src/browser/browser.ts
Chromium dependencies are installed via playwright install-deps chromium instead of a symlinked binary; openBrowser removes the Playwright chromium import, adds sign-in/first-run suppression flags, and always launches through CloakBrowser's persistent context with appended gpuArgs, regardless of meetingProvider.

Teams Meeting Page Navigation Blocking & Click Handling

Layer / File(s) Summary
External navigation blocking init script
src/meeting/teams.ts
openMeetingPage injects an init script overriding window.open, location.assign/replace, href/src setters, setAttribute, and anchor clicks to block navigation to external URLs, logging blocked attempts.
Click detection/humanization rework
src/meeting/teams.ts
clickWithInnerText's page.evaluate now only detects matching elements; a new clickButtonHumanized helper attempts locator-based clicks with a DOM-click fallback.
Logging format cleanups
src/meeting/teams.ts
Several console log/error statements are reformatted to multi-line forms using formatError, without logic changes.

Teams HTML Cleanup Stylesheet

Layer / File(s) Summary
Cleanup stylesheet injection
src/meeting/teams/htmlCleaner.ts
A guarded injectCleanupStylesheet helper adds CSS hiding Teams toast/alert UI, invoked from both removeInitialShityHtml and removeShityHtml.

Recording Sync Timing & Video Offset Calculation

Layer / File(s) Summary
ScreenRecorder timestamp tracking and offset computation
src/recording/ScreenRecorder.ts
recordingStartTime/syncSignalTimestamp are tracked on the instance and used to compute expectedSyncSec and a clamped calcOffsetVideo trim value, with added warning logging.
Window-based audio/video sync detection
src/utils/CalculVideoOffset.ts, src/utils/SyncSignal.ts
calculateVideoOffset gains an expectedSyncSec parameter used to bound audio/video search windows in detectAudioBeep/detectVideoFlash, missing-signal fallback logic is split into both-vs-one-missing cases, and the sync flash overlay opacity is increased to 1.

State Machine Flow Adjustments

Layer / File(s) Summary
Non-blocking HTML cleanup and speaker observation startup
src/state-machine/states/in-call-state.ts
setupBrowserComponents kicks off HTML cleanup and chained speaker observation without awaiting, logging errors non-fatally, and emits Events.inCallRecording before those tasks complete.
Preserve existing end reason on recorder error
src/state-machine/states/recording-state.ts
The screen recorder 'error' handler only sets StreamingSetupFailed when no end reason already exists.
Waiting-room timing control and dehumanize gating
src/state-machine/states/waiting-room-state.ts
The Meet-specific timing-control callback detects redirects away from the meeting and sets BotNotAccepted, and dehumanize is now called whenever a playwrightPage exists rather than only for Meet.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: amr-meetingbaas, Yusuf023

Poem

Hop, hop, through code we go,
CloakBrowser now the only flow,
Blocked links can't lead astray,
Sync flashes brighter light the way,
A tidy stylesheet, timings true —
This bunny's proud of what we drew! 🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: moving Teams setup off the recording critical path.
Description check ✅ Passed The description is clearly related to the changeset and matches the main Teams/CloakBrowser and non-blocking setup changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/v1-onprem-teams-nonblocking-20260702

Comment @coderabbitai help to get the list of available commands.

@Lazare-42

Copy link
Copy Markdown
Contributor Author

@coderabbitai

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@Lazare-42 It looks like your message just tagged me without a specific request. Could you let me know what you'd like help with? For example, I can:

  • Review the current changes in this PR
  • Answer questions about the CloakBrowser-based Teams join flow, Chrome runtime removal, or the non-blocking cleanup/observation logic
  • Summarize the PR changes
  • Help with anything else related to this PR

Just let me know!

( ̄ω ̄;) hmm, what did you have in mind?

@Lazare-42

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/recording/ScreenRecorder.ts (1)

1245-1266: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Anchor the final trim to the sync flash, not recordingStartTime.
(meetingStartTime - recordingStartTime) can over-trim by the FFmpeg capture-init gap and clip the beginning of the meeting. Use the detected flash in video time, e.g. syncResult.videoTimestamp + (meetingStartTime - syncSignalTimestamp) / 1000, or another capture-start adjustment for the final cut.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/recording/ScreenRecorder.ts` around lines 1245 - 1266, The final trim in
ScreenRecorder should not be anchored to recordingStartTime because it can
over-trim by the capture-init gap; instead, compute the cut from the detected
sync flash using syncResult.videoTimestamp plus the
meetingStartTime-to-sync-signal offset (or an equivalent capture-start
adjustment). Update the offset calculation and the related debug/warn logic in
the trimming block so the final cut is based on the flash timing rather than the
raw recording start.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/browser/browser.ts`:
- Around line 39-47: The Chrome launch args in browser.ts are using repeated
--disable-features switches, which causes earlier disabled features to be
overridden. Update the browser launch options so the feature list in the
relevant args array is merged into a single --disable-features entry, and make
sure the Browser/browser launch setup still includes SigninInterception,
IdentityConsistency, ChromeBrowserCloudManagement, SignInPromo,
ChromeWhatsNewUI, and AccountConsistency together.

In `@src/state-machine/states/waiting-room-state.ts`:
- Around line 189-208: Extract the inline async redirect-check callback passed
to handleTimingControl in waiting-room-state’s waiting-room flow into a small
private method such as checkMeetRedirectAway on the same class. Keep the
existing behavior exactly the same, including the url() check, logging,
GLOBAL.setShouldRetry, and GLOBAL.setError with MeetingEndReason.BotNotAccepted,
and pass the new named method into handleTimingControl instead of the nested
ternary callback.

---

Outside diff comments:
In `@src/recording/ScreenRecorder.ts`:
- Around line 1245-1266: The final trim in ScreenRecorder should not be anchored
to recordingStartTime because it can over-trim by the capture-init gap; instead,
compute the cut from the detected sync flash using syncResult.videoTimestamp
plus the meetingStartTime-to-sync-signal offset (or an equivalent capture-start
adjustment). Update the offset calculation and the related debug/warn logic in
the trimming block so the final cut is based on the flash timing rather than the
raw recording start.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6a9378fe-c386-4f17-94d4-91e8653116e1

📥 Commits

Reviewing files that changed from the base of the PR and between dde59df and e0a2e80.

📒 Files selected for processing (10)
  • Dockerfile
  • src/browser/browser.ts
  • src/meeting/teams.ts
  • src/meeting/teams/htmlCleaner.ts
  • src/recording/ScreenRecorder.ts
  • src/state-machine/states/in-call-state.ts
  • src/state-machine/states/recording-state.ts
  • src/state-machine/states/waiting-room-state.ts
  • src/utils/CalculVideoOffset.ts
  • src/utils/SyncSignal.ts

Comment thread src/browser/browser.ts
Comment on lines +39 to +47
// Suppress Chrome's "Sign in to Chrome?" / "Turn on sync" dialogs that
// can appear when an authenticated browser session is used.
'--no-first-run',
'--no-default-browser-check',
'--disable-sync',
'--disable-component-update',
'--disable-signin',
'--disable-features=SigninInterception,IdentityConsistency,ChromeBrowserCloudManagement,SignInPromo,ChromeWhatsNewUI,AccountConsistency',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Chromium multiple --disable-features command line only last one used

💡 Result:

In Chromium, the --disable-features and --enable-features command-line flags are not cumulative [1][2]. When these flags are specified multiple times on the same command line, only the value associated with the final occurrence is typically respected by the browser [2][3]. This behavior occurs because Chromium's command-line parsing logic [3][4] stores switches in a map structure where duplicate keys result in the previous value being overwritten by the last one encountered [3]. Consequently, if you provide --disable-features=FeatureA followed by --disable-features=FeatureB, Chromium will only disable FeatureB [2]. To effectively disable or enable multiple features, you must aggregate them into a single, comma-separated string within one instance of the flag (e.g., --disable-features=FeatureA,FeatureB) [5][1]. If you need to manage complex configurations across different sources (like environment variables or configuration files), you must programmatically combine the feature lists into a single command-line argument before launching the browser [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file around the cited lines.
git ls-files src/browser/browser.ts
wc -l src/browser/browser.ts
cat -n src/browser/browser.ts | sed -n '1,140p'

Repository: Meeting-BaaS/meet-teams-bot

Length of output: 7529


Merge the repeated --disable-features flags

Chromium treats repeated --disable-features switches as non-cumulative, so the sign-in suppression list here is overridden by the later --disable-features entries. Fold all disabled features into a single flag so SigninInterception,IdentityConsistency,ChromeBrowserCloudManagement,SignInPromo,ChromeWhatsNewUI,AccountConsistency actually take effect.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/browser/browser.ts` around lines 39 - 47, The Chrome launch args in
browser.ts are using repeated --disable-features switches, which causes earlier
disabled features to be overridden. Update the browser launch options so the
feature list in the relevant args array is merged into a single
--disable-features entry, and make sure the Browser/browser launch setup still
includes SigninInterception, IdentityConsistency, ChromeBrowserCloudManagement,
SignInPromo, ChromeWhatsNewUI, and AccountConsistency together.

Comment on lines +189 to +208
const startTime = await handleTimingControl(
GLOBAL.get().start_time,
isMeet
? async () => {
const url = this.context.playwrightPage?.url() ?? ''
if (url && !url.includes('meet.google.com')) {
console.log(
`Page navigated away from Meet during timing wait: ${url}`,
)
GLOBAL.setShouldRetry(true)
GLOBAL.setError(
MeetingEndReason.BotNotAccepted,
'Google Meet denied entry - page redirected during scheduled wait',
)
return true
}
return false
}
: undefined,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the inline redirect-check callback into a named method.

The anonymous async arrow function nested inside a ternary argument adds cognitive overhead. Extracting it to a small private method (e.g. checkMeetRedirectAway) would improve readability without behavior change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/state-machine/states/waiting-room-state.ts` around lines 189 - 208,
Extract the inline async redirect-check callback passed to handleTimingControl
in waiting-room-state’s waiting-room flow into a small private method such as
checkMeetRedirectAway on the same class. Keep the existing behavior exactly the
same, including the url() check, logging, GLOBAL.setShouldRetry, and
GLOBAL.setError with MeetingEndReason.BotNotAccepted, and pass the new named
method into handleTimingControl instead of the nested ternary callback.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

rg -n "handleTimingControl|meet.google.com|BotNotAccepted|shouldRetry|start_time" src

Repository: Meeting-BaaS/meet-teams-bot

Length of output: 6875


🏁 Script executed:

ast-grep outline src/utils/timing-control.ts --view expanded
ast-grep outline src/meeting/meet.ts --view expanded
sed -n '1,220p' src/utils/timing-control.ts
sed -n '1,120p' src/meeting/meet.ts
sed -n '400,470p' src/meeting/meet.ts

Repository: Meeting-BaaS/meet-teams-bot

Length of output: 12099


🏁 Script executed:

sed -n '180,220p' src/state-machine/states/waiting-room-state.ts

Repository: Meeting-BaaS/meet-teams-bot

Length of output: 2059


Compare the hostname here instead of using includes. page.url() can include meet.google.com in a path or redirect param on a non-Meet page, so this check can miss a real redirect away from Meet. new URL(url).hostname === 'meet.google.com' makes the denial check exact.

Chromium treats repeated feature-list switches as single-valued. Keep disabled feature lists in one flag so later entries do not overwrite earlier launch options.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant