Add Google Meet recording + transcript archival#39
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds Google Meet conference discovery and archival support alongside existing Zoom archival, including config mapping and UI affordances for Meet-only installs.
Changes:
- Introduces a
meetpackage client to list conferences and their Drive-backed artifacts (recordings/transcripts). - Extends config/directives and archival pipeline to map Meet meeting codes to Drive folders and copy artifacts.
- Updates mux, startup, docs, and tests to support optional Zoom and Meet flows.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| meet/mock/mock.go | Adds an HTTP mock for the Meet v2 endpoints used by tests. |
| meet/meet.go | Implements Meet API client, pagination, and artifact extraction. |
| meet/meet_test.go | Adds unit tests for client auth/decoding and Meet listing/pagination behavior. |
| main.go | Integrates Meet into config, UI mux, archival workflow, and CLI/boot logic. |
| main_test.go | Updates config wiring tests and adds Meet-specific unit tests and mux coverage for Meet-only installs. |
| google/google.go | Adds Meet OAuth scope and a lazy token source for downstream Google APIs. |
| google/google_test.go | Verifies Meet scope inclusion and lazy token source behavior. |
| README.md | Documents Meet support, scope changes, and configuration. |
| go.sum | Updates module sums accordingly. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // ApiHandler mocks the Google Meet REST API v2 endpoints used by ListConferences. | ||
| func ApiHandler(t *testing.T) http.HandlerFunc { |
There was a problem hiding this comment.
Leaving as ApiHandler for consistency: the repo already uses ApiHandler/OauthHandler in zoom/mock and google/mock, so matching that keeps the test mocks uniform. Happy to do a repo-wide ApiHandler -> APIHandler rename in a separate cleanup if preferred.
| "encoding/json" | ||
| "errors" | ||
| "fmt" | ||
| "io/ioutil" |
There was a problem hiding this comment.
Keeping io/ioutil for consistency: zoom.go and google.go both use ioutil.ReadAll, and go.mod declares go 1.12 (io.ReadAll is 1.16+). Switching just meet/ would be inconsistent; happy to migrate all three in a separate cleanup with a go-directive bump.
| if body, rerr := ioutil.ReadAll(rsp.Body); rerr == nil { | ||
| msg += ": " + string(body) | ||
| } |
There was a problem hiding this comment.
Same as the other ioutil note: kept for consistency with zoom.go/google.go and the go 1.12 directive (io.ReadAll is 1.16+). Can migrate all three together in a follow-up.
5f7a454 to
37fb79c
Compare
| config, err := google.ConfigFromJSON(b, drive.DriveScope) | ||
| // drive scope for archival, meet scope for conference/recording discovery | ||
| config, err := google.ConfigFromJSON(b, drive.DriveScope, | ||
| "https://www.googleapis.com/auth/meetings.space.readonly") |
There was a problem hiding this comment.
Should this be added with an option or config? Otherwise users who only care about Zoom recordings will be prompted for unnecessary permissions
There was a problem hiding this comment.
Good point. Made the Meet scope opt-in via config: zat now requests meetings.space.readonly (and builds the Meet client) only when zat.yml has at least one meet: directive. Drive/Zoom-only users get the Drive scope alone and are never prompted for Meet permissions.
Implemented as a google.WithMeetScope() client option that main() applies only when meetConfigured(directives) is true; the default ConfigFromJSON is back to just drive.DriveScope. README updated to note the scope is opt-in and that re-login is only needed when you first add a meet: directive.
Mirror the existing Zoom->Drive capability for Google Meet. Because Meet recordings already auto-save to Google Drive, archiving is a Drive Files.Copy of artifacts already in Drive into the mapped folder rather than a download. The copy is owned by the account zat authenticates as, so it both organizes recordings into the right shared folder and preserves them if the original host later leaves the org. - New meet/ package: Meet REST API discovery over raw HTTP (mirrors zoom/, as the pinned google.golang.org/api predates the Meet client). ListConferences enumerates conference records, resolves space -> meetingCode, and lists recordings + transcripts as copyable Drive file IDs. Built from an oauth2.TokenSource, leaving a clean seam for future domain-wide delegation. - Meet is opt-in: the meetings.space.readonly scope and the Meet client are only requested when zat.yml has meet: directives, so Drive/Zoom-only users are never prompted for Meet permissions. google.Client.TokenSource (added here) resolves credentials lazily so it works across a later web login or refresh, and is guarded by a RWMutex since the background archiver reads the token while the OAuth handler may be writing it. - zat.yml gains a meet: key (meeting code); a directive may set zoom:, meet:, or both. Reuses the existing per-meeting folder naming, name-based dedupe, -t filter (recording/transcript for Meet), -since window, -min-duration skip, and Slack notify on a copied recording. - Zoom is now optional: a missing zoom config disables Zoom archival and logs a notice instead of aborting, so a Meet-only install runs on Google creds alone. - Web UI: a Meet status line (shown only when Meet is configured) and a /meet debug endpoint (503 when Meet is not configured, redirect to login when unauthenticated, JSON otherwise). - Skip unmapped/unrecorded conferences quietly (they are the common case since ListConferences returns every attended meeting) and avoid empty Drive folders. - Surface real config-load errors (the old os.IsExist guard swallowed them) while still tolerating a missing config file. Tests cover Meet discovery (happy path, pagination across all three list endpoints, start_time filter, space and not-yet-in-Drive skips), the meetCopies mapping, Meet detection/opt-in scope, Meet file naming, the lazy TokenSource, and the web UI (Meet-only path plus /meet auth, success, and error handling). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
37fb79c to
b33175d
Compare
|
@pmoust I'm not invested in Google Meet support, so take with a grain of salt, but I wonder if you'd be better off with less code and more cloud services? @graphaelli might have a more informed opinion. An alternative solution would be to create a subscription to Google Workspace events, filtered to Google Meet events. You can then write a little bit of code deployed to Cloud Run that consumes those events with a Pub/Sub topic push subscription. With that approach you should be able to more easily deal with delays between video and transcript upload, since it will all be event-based. |
|
Yeah this does make sense. The downside is some extra plumbing needed, which for some startups' nature is not super trivial. The use-case I have is standardizing across a set of tech and non-tech startups I advise. So Don't feel any pressure to merge this in (afterall it is zat), I can just keep it in my fork. Closing this one, and if @graphaelli feels its worth it, he can re-open it ;) |
|
I like the idea a lot and could actually use this as well, as I'm in one of those Google Meet-only teams these days too. There's a bit of a mix of concerns here with things like support for concurrent credential updates with the core meet functionality so I'm happy to pick carry this forward unless you want to @pmoust. |
|
Feel free @graphaelli - my needs are covered from this branch (merged in my fork now), so I wasn't planning on working on it further. |

Mirror the existing Zoom->Drive capability for Google Meet. Because Meet recordings already auto-save to Google Drive, archiving is a Drive Files.Copy of artifacts already in Drive into the mapped folder rather than a download. The copy is owned by the account zat authenticates as, so it both organizes recordings into the right shared folder and preserves them if the original host later leaves the org.
Tests cover Meet discovery (happy path, pagination, start_time filter, space and not-yet-in-Drive skips), the meetCopies mapping, Meet file naming, the added Google scope and lazy TokenSource, and the Meet-only web UI path.