Skip to content

Improve Google Meet meeting-end logic and preserve local recordings on upload failure#36

Open
nikhil2882 wants to merge 5 commits into
screenappai:mainfrom
nikhil2882:main
Open

Improve Google Meet meeting-end logic and preserve local recordings on upload failure#36
nikhil2882 wants to merge 5 commits into
screenappai:mainfrom
nikhil2882:main

Conversation

@nikhil2882
Copy link
Copy Markdown

@nikhil2882 nikhil2882 commented Apr 8, 2026

Summary

  • refactors Google Meet meeting-end behavior into modular components (SilenceMonitor, ParticipantStateResolver, MeetingEndDecisionEngine)
  • adds silence-triggered participant checks with env-configurable thresholds and fallback behavior
  • introduces recording retention policy so temp files are deleted only after successful upload
  • adds tests for meeting-end modules and retention policy
  • adds local recording retrieval endpoints for OSS/self-hosted usage when object storage is not configured

Why

  • prevents premature meeting exit caused by fragile participant DOM detection
  • avoids recording loss for OSS/local deployments when remote upload is unavailable or fails
  • improves local operability by allowing retrieval of retained recordings from temp storage as If someone dont want to use S3, then they can use the server storage

Config changes

  • MEETING_INACTIVITY_MINUTES (default 5)
  • MEETING_END_FALLBACK_SILENCE_MINUTES (default 15)
  • ENABLE_PARTICIPANT_COUNT_END (default true)

New recording routes (local/temp storage)

All routes are mounted under app.use('/recordings', recordingsRouter).

  • GET /recordings
    • lists available recording folders/files from dist/_tempvideo in UI
  • GET /recordings/:userId
    • lists recordings for a specific user folder in UI
  • GET /recordings/:userId/:filename
    • downloads a specific recording file from a user folder
  • GET /recordings/_root/:filename
    • downloads a recording directly under the temp root folder

These endpoints are intended as fallback access for local/OSS deployments where remote object storage is not configured or upload fails.

Validation

  • yarn test --run passes locally

What changed in code (and expected outcome)

1) Meeting-end logic is now modular and explicit

  • Added:
    • src/lib/meeting-end/SilenceMonitor.ts
    • src/lib/meeting-end/ParticipantStateResolver.ts
    • src/lib/meeting-end/MeetingEndDecisionEngine.ts
    • src/lib/meeting-end/types.ts
  • Integrated these modules into src/bots/GoogleMeetBot.ts recording flow.

What this yields

  • Clear separation of concerns:
    • audio silence tracking
    • participant state resolution
    • end-decision policy
  • Safer behavior under DOM instability:
    • participant checks are silence-gated
    • unknown participant state does not force immediate exit
  • Future strategy extension is easier:
    • participant resolver can add more strategies without changing bot orchestration.

2) Silence/fallback behavior is configurable through env

  • Updated src/config.ts and .env.example with:
    • MEETING_INACTIVITY_MINUTES (default 5)
    • MEETING_END_FALLBACK_SILENCE_MINUTES (default 15)
    • ENABLE_PARTICIPANT_COUNT_END (default true)

What this yields

  • Better default behavior for long meetings.
  • Predictable fallback behavior in low-signal/unknown participant detection cases.
  • Easier operator tuning without code changes.

3) Timer/interval cleanup tightened in Google Meet flow

  • In GoogleMeetBot, cleanup hooks stop silence monitoring and browser-side intervals when meeting end is decided.

What this yields

  • Reduced risk of post-end background checks continuing to run.
  • Cleaner lifecycle and lower chance of noisy post-end logs.

4) Recording retention policy prevents data loss

  • Added:
    • src/lib/storage/retention.ts
    • src/lib/storage/types.ts
  • Wired into src/middleware/disk-uploader.ts to:
    • delete temp file only on confirmed upload success
    • retain temp file on upload failure or unsupported uploader config

What this yields

  • Local recordings are preserved for OSS/self-hosted users when remote upload is unavailable.
  • Recovery path exists even when object storage integration fails.

5) Local recording access routes added

  • Added src/app/recordings.ts and mounted in src/app/index.ts.
  • Routes:
    • GET /recordings
    • GET /recordings/:userId
    • GET /recordings/:userId/:filename
    • GET /recordings/_root/:filename

What this yields

  • Operators can browse/download retained temp recordings directly.
  • Improves local debugging and manual recovery workflow.

6) Tests added for new modules/policies

  • Added tests:
    • src/test/__tests__/SilenceMonitor.test.ts
    • src/test/__tests__/ParticipantStateResolver.test.ts
    • src/test/__tests__/MeetingEndDecisionEngine.test.ts
    • src/test/__tests__/RecordingRetentionPolicy.test.ts
    • plus vitest config and import checks.

What this yields

  • Better confidence for regression safety around end-decision and retention behavior.

Exit strategy flow (how meeting termination now works)

The bot now uses a decision pipeline instead of a single DOM check.

  1. Silence is monitored continuously

    • SilenceMonitor samples audio level on an interval.
    • While audio is active, silence counters reset.
    • While audio stays below threshold, cumulative silence grows.
  2. Primary silence threshold triggers participant checks

    • After MEETING_INACTIVITY_MINUTES (default 5) of sustained silence, participant checks are enabled.
    • ParticipantStateResolver evaluates page state and returns one of:
      • participants_present
      • alone_confirmed
      • removed_from_meeting
      • page_state_changed
      • unknown
  3. Decision engine applies end rules

    • participants_present -> continue recording.
    • alone_confirmed -> requires repeated confirmations (streak-based) before ending.
    • removed_from_meeting / stable page_state_changed -> end with explicit reason.
    • unknown -> do not end immediately; continue monitoring.
  4. Fallback silence threshold is the safety net

    • If uncertainty persists and cumulative silence reaches MEETING_END_FALLBACK_SILENCE_MINUTES (default 15), meeting ends with fallback_silence.
  5. Hard-stop conditions still apply

    • Browser end signal -> browser_signal
    • Max duration timeout -> max_duration
  6. On any end decision

    • Meeting end reason is recorded.
    • silence/timer/interval loops are cleaned up.
    • recorder is stopped and upload/retention flow starts.

This design reduces false-positive exits from fragile DOM-only participant detection while still guaranteeing eventual termination under long silence.

Notes

  • this PR also includes prior commits already on the branch (ea29afc, 303bb52, ec50c7c, 309f61f)
  • follow-up cleanup can split concerns into smaller PRs if maintainers prefer

- Introduced a new `recordings.ts` file to manage routes for accessing and downloading recordings.
- Implemented functionality to list recordings in user directories and root, with appropriate HTML responses.
- Added validation for user IDs and filenames to ensure secure access to recordings.
- Updated `index.ts` to include the new recordings router in the application.
…lseAudio checks

- Replaced the existing recording method with a new implementation using FFmpeg for improved performance.
- Added checks for PulseAudio status and socket existence to ensure proper audio handling.
- Updated browser context creation to set the PulseAudio environment for Google Meet bot.
- Cleaned up unused imports and logging for better clarity and maintainability.
…t bot

- Introduced a new `SilenceMonitor` class for audio silence detection, enhancing meeting end decision-making.
- Implemented `ParticipantStateResolver` to determine participant presence and state changes.
- Added `MeetingEndDecisionEngine` to manage meeting end logic based on silence and participant states.
- Updated `GoogleMeetBot` to utilize the new classes for improved reliability in meeting management.
- Enhanced configuration options in `.env.example` for inactivity detection and participant count handling.
- Added tests for new functionality to ensure robustness and reliability.
…ID generation

- Replaced `encodeFileNameSafebase64` with `generateTempFileId` in Google Meet, Microsoft Teams, and Zoom bots for improved clarity and consistency in temporary file ID creation.
- Added a new utility function `generateTempFileId` to encapsulate the logic for generating unique temporary file IDs.
- Enhanced the recordings router to escape HTML in user folder and file names for better security and presentation.
- Introduced a new route to serve files from the temporary directory with validation for filename segments to prevent directory traversal attacks.
@ddilushan
Copy link
Copy Markdown
Collaborator

@nikhil2882 Can you Split into multiple PRs, probably like this: (a) meeting-end decision modules + tests + retention policy, (b) Google Meet FFmpeg capture switchover (with streaming upload, not buffered), (c) /recordings listing route (with auth + TTL)

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.

2 participants