From 4138bfc36dca59cd510a1bc300688d1403783f9c Mon Sep 17 00:00:00 2001 From: Adam Weeks Date: Tue, 24 Feb 2026 09:42:48 -0500 Subject: [PATCH 1/2] Add CODE_TOUR.md and fix repo URL to WebexSamples/meetings-exporter Co-authored-by: Cursor --- CODE_TOUR.md | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 2 +- README.md | 4 +- 3 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 CODE_TOUR.md diff --git a/CODE_TOUR.md b/CODE_TOUR.md new file mode 100644 index 0000000..f9232de --- /dev/null +++ b/CODE_TOUR.md @@ -0,0 +1,117 @@ +# Code Tour: Webex Meeting Data Exporter + +A quick walkthrough of the codebase and how it maps to Webex meeting APIs. + +## Overview + +This project exports Webex meeting data (recordings, transcripts, summaries, action items) to local folders or Google Drive. It uses two Webex API families: + +1. **Webex Meetings API** – meetings, participants, recordings, transcripts +2. **Meeting Summaries API** – AI-generated summaries and action items + +## Flow: CLI → Ingestion → Webex → Export + +``` +CLI (list/export) → WebexClient (HTTP) → Ingestion (collect_meeting_data) → MeetingData → Exporter (write) +``` + +## 1. Entry Point: `cli.py` + +- **`list`** – Lists past meetings in a date range +- **`export`** – Exports one meeting by ID or all meetings in a date range + +Both commands use `WebexClient` and `export_meeting()` from the ingestion layer. + +## 2. Webex API Client: `webex_client.py` + +All Webex calls go through `WebexClient`. Base URL: `https://webexapis.com/v1`. + +| Method | Webex API Endpoint | Purpose | +|--------|--------------------|---------| +| `list_meetings()` | `GET /meetings` | List meetings with `meetingType=meeting` (instances only) | +| `get_meeting()` | `GET /meetings/{id}` | Get a single meeting | +| `list_meeting_participants()` | `GET /meetingParticipants?meetingId=...` | List participants | +| `list_recordings()` | `GET /recordings?meetingId=...` | List recordings | +| `list_meeting_transcripts()` | `GET /meetingTranscripts?meetingId=...` | List transcripts | +| `get_meeting_summary_by_meeting_id()` | `GET /meetingSummaries?meetingId=...` | Get AI summary and action items | +| `download_transcript_from_item()` | `GET {txtDownloadLink}` | Download transcript (URL from transcript item) | + +**Important details:** + +- **Instance IDs** – Summaries, recordings, and transcripts require instance IDs (e.g. `..._I_...`), not series IDs. `list_meetings()` uses `meetingType=meeting` to return instances. +- **Meeting Summaries API** – Uses `GET /meetingSummaries?meetingId=...` (not the Summary Report API). See [Get Summary by Meeting ID](https://developer.webex.com/meeting/docs/api/v1/meeting-summaries/get-summary-by-meeting-id). +- **Recordings** – Uses `downloadUrl` from the recordings response for binary download. + +## 3. Ingestion: `ingestion.py` + +`collect_meeting_data()` fetches everything for a meeting and returns a normalized `MeetingData` object: + +1. **Meeting details** – `get_meeting()` → title, start/end, host, etc. +2. **Participants** – `list_meeting_participants()` +3. **Recordings** – `list_recordings()` → `downloadUrl` → `_get_binary()` +4. **Transcript** – `list_meeting_transcripts()` → `txtDownloadLink` → download +5. **Summary & action items** – `get_meeting_summary_by_meeting_id()` → parses `summary`, `actionItems` (or `action_items`) + +`export_meeting()` orchestrates: `collect_meeting_data()` → then passes `MeetingData` to the exporter. + +## 4. Data Model: `models.py` + +| Model | Role | +|-------|------| +| `MeetingData` | Normalized meeting data (title, times, recordings, transcript, summary, action items, participants) | +| `RecordingAsset` | Recording file (filename, content, download_url, mime_type) | +| `ActionItem` | Action item (text, assignee, due, raw) | + +## 5. Formatting: `meeting_formatter.py` + +Shared formatting used by exporters: + +- `folder_name()` – e.g. `2026-02-05 14-00 - Meeting Title` +- `meeting_details_text()` – Content for `meeting_details.txt` +- `summary_txt_content()` – Summary + action items for `summary.txt` +- `safe_filename()` – Sanitizes names for file/folder use + +## 6. Exporters: `exporters/` + +| File | Class | Role | +|------|-------|------| +| `base.py` | `MeetingExporter` | ABC with `write(MeetingData) -> str` | +| `local_folder.py` | `LocalFolderExporter` | Writes to disk (meeting_details.txt, transcript.vtt, summary.txt, summary.json, recordings) | +| `google_drive.py` | `GoogleDriveExporter` | Uploads same structure to Google Drive | +| `factory.py` | `get_exporter()` | Chooses exporter from `EXPORT_BACKEND` env var | + +## 7. Webhook Utilities: `webhook_utils.py` + +Helpers for future webhook support: + +- `extract_meeting_id_from_payload()` – Gets `meetingId` from Webex webhook payloads +- `extract_recording_id_from_payload()` – Gets `recordingId` from payloads + +## Webex API → Output Mapping + +| Webex API Data | Output File / Content | +|----------------|------------------------| +| `GET /meetings/{id}` | `meeting_details.txt` (title, date, host, participants) | +| `GET /meetingParticipants` | Participant list in `meeting_details.txt` | +| `GET /recordings` + `downloadUrl` | `recording_*.mp4` (or `.webm`, `.m4a`) | +| `GET /meetingTranscripts` → `txtDownloadLink` | `transcript.vtt` or `transcript.txt` | +| `GET /meetingSummaries` | `summary.txt`, `summary.json`, action items in `summary.txt` | + +## File Layout (Per Meeting) + +``` +{output_dir}/ + 2026-02-05 14-00 - Meeting Title/ + meeting_details.txt # Title, date, host, participants + transcript.vtt # Or transcript.txt + summary.txt # Summary + action items + summary.json # Raw Meeting Summaries API response + recording_1.mp4 # Downloaded recordings +``` + +## Quick Reference: Webex Docs + +- [Meetings API](https://developer.webex.com/docs/meetings) +- [Meeting Summaries API – Get Summary by Meeting ID](https://developer.webex.com/meeting/docs/api/v1/meeting-summaries/get-summary-by-meeting-id) +- [Recordings](https://developer.webex.com/docs/meetings#recordings) +- [Transcripts](https://developer.webex.com/docs/meetings#meeting-transcripts) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8246bbe..2556ebf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Thanks for your interest in contributing. This document covers how to set up the 1. Clone the repository and create a virtual environment: ```bash -git clone https://github.com/webex/meetings-exporter.git +git clone https://github.com/WebexSamples/meetings-exporter.git cd meetings-exporter python3 -m venv .venv source .venv/bin/activate # On Windows: .venv\Scripts\activate diff --git a/README.md b/README.md index d26d229..b940db3 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Export Webex meeting data (recordings, transcripts, summaries, and action items) ## Quick Start ```bash -git clone https://github.com/webex/meetings-exporter.git +git clone https://github.com/WebexSamples/meetings-exporter.git cd meetings-exporter python3 -m venv .venv && source .venv/bin/activate pip install -e ".[dev]" @@ -49,7 +49,7 @@ Meetings must be **individual instances** (not series) to get summaries, recordi 1. **Clone the repository** ```bash -git clone https://github.com/webex/meetings-exporter.git +git clone https://github.com/WebexSamples/meetings-exporter.git cd meetings-exporter ``` From 7b4e00a4f1897eb680023f06d1519a2a9648749d Mon Sep 17 00:00:00 2001 From: Adam Weeks Date: Tue, 24 Feb 2026 10:36:34 -0500 Subject: [PATCH 2/2] Fix Prettier formatting in CODE_TOUR.md Co-authored-by: Cursor --- CODE_TOUR.md | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/CODE_TOUR.md b/CODE_TOUR.md index f9232de..cb1b84c 100644 --- a/CODE_TOUR.md +++ b/CODE_TOUR.md @@ -26,15 +26,15 @@ Both commands use `WebexClient` and `export_meeting()` from the ingestion layer. All Webex calls go through `WebexClient`. Base URL: `https://webexapis.com/v1`. -| Method | Webex API Endpoint | Purpose | -|--------|--------------------|---------| -| `list_meetings()` | `GET /meetings` | List meetings with `meetingType=meeting` (instances only) | -| `get_meeting()` | `GET /meetings/{id}` | Get a single meeting | -| `list_meeting_participants()` | `GET /meetingParticipants?meetingId=...` | List participants | -| `list_recordings()` | `GET /recordings?meetingId=...` | List recordings | -| `list_meeting_transcripts()` | `GET /meetingTranscripts?meetingId=...` | List transcripts | -| `get_meeting_summary_by_meeting_id()` | `GET /meetingSummaries?meetingId=...` | Get AI summary and action items | -| `download_transcript_from_item()` | `GET {txtDownloadLink}` | Download transcript (URL from transcript item) | +| Method | Webex API Endpoint | Purpose | +| ------------------------------------- | ---------------------------------------- | --------------------------------------------------------- | +| `list_meetings()` | `GET /meetings` | List meetings with `meetingType=meeting` (instances only) | +| `get_meeting()` | `GET /meetings/{id}` | Get a single meeting | +| `list_meeting_participants()` | `GET /meetingParticipants?meetingId=...` | List participants | +| `list_recordings()` | `GET /recordings?meetingId=...` | List recordings | +| `list_meeting_transcripts()` | `GET /meetingTranscripts?meetingId=...` | List transcripts | +| `get_meeting_summary_by_meeting_id()` | `GET /meetingSummaries?meetingId=...` | Get AI summary and action items | +| `download_transcript_from_item()` | `GET {txtDownloadLink}` | Download transcript (URL from transcript item) | **Important details:** @@ -56,11 +56,11 @@ All Webex calls go through `WebexClient`. Base URL: `https://webexapis.com/v1`. ## 4. Data Model: `models.py` -| Model | Role | -|-------|------| -| `MeetingData` | Normalized meeting data (title, times, recordings, transcript, summary, action items, participants) | -| `RecordingAsset` | Recording file (filename, content, download_url, mime_type) | -| `ActionItem` | Action item (text, assignee, due, raw) | +| Model | Role | +| ---------------- | --------------------------------------------------------------------------------------------------- | +| `MeetingData` | Normalized meeting data (title, times, recordings, transcript, summary, action items, participants) | +| `RecordingAsset` | Recording file (filename, content, download_url, mime_type) | +| `ActionItem` | Action item (text, assignee, due, raw) | ## 5. Formatting: `meeting_formatter.py` @@ -73,12 +73,12 @@ Shared formatting used by exporters: ## 6. Exporters: `exporters/` -| File | Class | Role | -|------|-------|------| -| `base.py` | `MeetingExporter` | ABC with `write(MeetingData) -> str` | +| File | Class | Role | +| ----------------- | --------------------- | ------------------------------------------------------------------------------------------- | +| `base.py` | `MeetingExporter` | ABC with `write(MeetingData) -> str` | | `local_folder.py` | `LocalFolderExporter` | Writes to disk (meeting_details.txt, transcript.vtt, summary.txt, summary.json, recordings) | -| `google_drive.py` | `GoogleDriveExporter` | Uploads same structure to Google Drive | -| `factory.py` | `get_exporter()` | Chooses exporter from `EXPORT_BACKEND` env var | +| `google_drive.py` | `GoogleDriveExporter` | Uploads same structure to Google Drive | +| `factory.py` | `get_exporter()` | Chooses exporter from `EXPORT_BACKEND` env var | ## 7. Webhook Utilities: `webhook_utils.py` @@ -89,13 +89,13 @@ Helpers for future webhook support: ## Webex API → Output Mapping -| Webex API Data | Output File / Content | -|----------------|------------------------| -| `GET /meetings/{id}` | `meeting_details.txt` (title, date, host, participants) | -| `GET /meetingParticipants` | Participant list in `meeting_details.txt` | -| `GET /recordings` + `downloadUrl` | `recording_*.mp4` (or `.webm`, `.m4a`) | -| `GET /meetingTranscripts` → `txtDownloadLink` | `transcript.vtt` or `transcript.txt` | -| `GET /meetingSummaries` | `summary.txt`, `summary.json`, action items in `summary.txt` | +| Webex API Data | Output File / Content | +| --------------------------------------------- | ------------------------------------------------------------ | +| `GET /meetings/{id}` | `meeting_details.txt` (title, date, host, participants) | +| `GET /meetingParticipants` | Participant list in `meeting_details.txt` | +| `GET /recordings` + `downloadUrl` | `recording_*.mp4` (or `.webm`, `.m4a`) | +| `GET /meetingTranscripts` → `txtDownloadLink` | `transcript.vtt` or `transcript.txt` | +| `GET /meetingSummaries` | `summary.txt`, `summary.json`, action items in `summary.txt` | ## File Layout (Per Meeting)