Skip to main content

CLI tool to download Zoom cloud recordings and extract audio for transcription

Project description

dlzoom

Download Zoom cloud recordings from the command line.

Simple CLI tool to download audio recordings and metadata from Zoom meetings using meeting IDs.

Features

  • 🎵 Download audio recordings (M4A format)
  • 📝 Download transcripts, chat logs, and timelines
  • 🔄 Automatic audio extraction from video files (MP4 → M4A)
  • 🔐 Authentication: Hosted user OAuth (default) or Server-to-Server OAuth
  • 📋 JSON output for automation
  • 🎯 Support for recurring meetings and PMI
  • ⏳ Wait for recording processing with --wait
  • 🔍 Check recording availability before downloading
  • 🛡️ Secure (credentials never exposed in logs)
  • 🔁 Automatic retry with exponential backoff
  • 💪 Production-ready with 119 tests

Installation

Choose your preferred method:

🚀 Quick Try (uvx - Instant Run, No Install)

Fastest way to try dlzoom - requires Python 3.11+ and ffmpeg:

# Install uv first (if not installed)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Run dlzoom instantly (no installation needed!)
uvx dlzoom download 123456789 --check-availability

📦 PyPI Install (Recommended for Regular Use)

# Install with pip
pip install dlzoom

# Or with uv (10-100x faster)
uv pip install dlzoom

# Or with uv tool (isolated installation)
uv tool install dlzoom

Note: Requires Python 3.11+ and ffmpeg (see below).

🐳 Docker (Zero Dependencies - Everything Included!)

Best for: Production, CI/CD, no local dependencies

# Run with Docker (includes Python + ffmpeg)
docker run -it --rm \
  -v $(pwd)/recordings:/app/downloads \
  -e ZOOM_ACCOUNT_ID="your_account_id" \
  -e ZOOM_CLIENT_ID="your_client_id" \
  -e ZOOM_CLIENT_SECRET="your_secret" \
  yanivgolan1/dlzoom:latest \
  download 123456789

Or use GitHub Container Registry:

docker run -it --rm \
  -v $(pwd)/recordings:/app/downloads \
  -e ZOOM_ACCOUNT_ID="your_account_id" \
  -e ZOOM_CLIENT_ID="your_client_id" \
  -e ZOOM_CLIENT_SECRET="your_secret" \
  ghcr.io/yaniv-golan/dlzoom:latest \
  download 123456789

🔧 From Source (Development)

# Clone the repository
git clone https://github.com/yaniv-golan/dlzoom.git
cd dlzoom

# Create virtual environment (recommended)
python3.11 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install
pip install -e .

Prerequisites (for non-Docker installations)

  • Python 3.11+ (required)
  • ffmpeg (required for audio extraction from video files)

Install ffmpeg:

# macOS
brew install ffmpeg

# Ubuntu/Debian
sudo apt install ffmpeg

# Windows (via Chocolatey)
choco install ffmpeg

# Windows (via winget)
winget install ffmpeg

Docker users: No need to install Python or ffmpeg - everything is included!

Quick Start

1. Sign In (Recommended)

Use our hosted authentication service to connect your Zoom account (no secrets required):

dlzoom login

This opens your browser to approve access and stores a short‑lived token locally (refreshed automatically).

Alternatively, organizational users can configure Server‑to‑Server (S2S) OAuth using environment variables or a config file.

Host your own broker (optional)

  • The CLI uses a hosted sign‑in broker by default. To self‑host, deploy the worker under zoom-broker/ (see that README), then point dlzoom to your URL via the --auth-url option or environment variable when logging in.

2. Configure S2S Credentials (Optional)

Create a .env file in your working directory:

ZOOM_ACCOUNT_ID=your_account_id_here
ZOOM_CLIENT_ID=your_client_id_here
ZOOM_CLIENT_SECRET=your_client_secret_here

⚠️ Security Warning: If your project directory is in a cloud-synced folder (iCloud, Dropbox, Google Drive, etc.), your .env file containing credentials may be uploaded to cloud storage. Consider using environment variables instead, moving the project to a non-synced directory, or using a config file outside the project directory with --config.

Or set environment variables:

export ZOOM_ACCOUNT_ID="your_account_id"
export ZOOM_CLIENT_ID="your_client_id"
export ZOOM_CLIENT_SECRET="your_client_secret"

Or use a config file:

# config.yaml
zoom_account_id: "your_account_id"
zoom_client_id: "your_client_id"
zoom_client_secret: "your_client_secret"
log_level: "INFO"

Automatic .env loading and opt-out

  • dlzoom automatically loads environment variables from a .env file at startup. It searches from your current working directory upwards (like git), and loads the first .env it finds.
  • Loading uses override=False, so variables already present in your shell/environment take precedence over values in .env.
  • To disable auto-loading (e.g., for CI or scripts that must be fully deterministic), set:
export DLZOOM_NO_DOTENV=1

Tips and caveats:

  • Prefer explicit environment variables for automation where you don’t want a parent directory .env to influence behavior.
  • If your project lives in a cloud‑synced folder (Dropbox, iCloud, Google Drive), treat .env as sensitive; consider using environment variables or a config file stored outside that folder.

3. Browse and Download

Browse your recordings by date:

# Last 7 days
dlzoom recordings --range last-7-days

# Specific window
dlzoom recordings --from-date 2025-01-01 --to-date 2025-01-31

# Filter by topic (user-wide mode)
dlzoom recordings --range today --topic "standup"

Inspect instances for a specific meeting (replaces the old download --list):

dlzoom recordings --meeting-id 123456789

Download a recording (audio + transcript + chat + timeline):

dlzoom download 123456789

Tip: You can paste meeting IDs directly from Zoom. Spaces are removed automatically:

dlzoom download "882 9060 9309"  # Works! Spaces are removed automatically
dlzoom download 88290609309      # Also works

Usage

Basic Commands

Check if recording is available:

dlzoom download 123456789 --check-availability

Use dlzoom recordings --meeting-id 123456789 instead of the removed download --list.

Download recording (audio + transcript + chat + timeline):

dlzoom download 123456789

Download with custom output name:

dlzoom download 123456789 --output-name "my_meeting"

Download to specific directory:

dlzoom download 123456789 --output-dir ~/Downloads/zoom

Wait for recording to finish processing:

dlzoom download 123456789 --wait 30

Advanced Options

Use config file:

dlzoom download 123456789 --config config.yaml

Verbose output (see detailed logs):

dlzoom download 123456789 --verbose

Debug mode (full API responses):

dlzoom download 123456789 --debug

JSON output (for automation):

dlzoom download 123456789 --json

Other Commands

  • Show current account:
dlzoom whoami
  • Sign out and remove local tokens:
dlzoom logout

Optional Permissions (Advanced)

dlzoom works with minimal permissions by default. You can optionally add these to improve fidelity:

Required Scopes (Minimum):

  • cloud_recording:read:list_user_recordings - List your cloud recordings
  • cloud_recording:read:list_recording_files - Access recording file details for download

Optional Scopes (Enhanced Features):

  • meeting:read:meeting - Definitively mark recurring meetings by checking meeting type; without it, recurrence is inferred only within the fetched date range
  • user:read:user - Show your name/email in whoami when using user tokens

All scopes use Zoom's granular scope naming (user-managed OAuth). Behavior degrades gracefully if optional scopes are not enabled.


**Dry run (see what would be downloaded):**

```bash
dlzoom download 123456789 --dry-run

Custom filename template:

dlzoom download 123456789\
  --filename-template "{topic}_{start_time:%Y%m%d}"

Custom folder structure:

dlzoom download 123456789\
  --folder-template "{start_time:%Y}/{start_time:%m}"

Select specific recording instance (for recurring meetings):

dlzoom download 123456789 --recording-id "abc123def456"

Skip Downloads

Skip transcript download:

dlzoom download 123456789 --skip-transcript

Skip chat log download:

dlzoom download 123456789 --skip-chat

Skip timeline download:

dlzoom download 123456789 --skip-timeline

Download Options

dlzoom download [OPTIONS] MEETING_ID

Options:
  --output-dir, -o PATH          Output directory (default: current directory)
  --output-name, -n TEXT         Base filename (default: meeting_id)
  --verbose, -v                  Show detailed operation information
  --debug, -d                    Show full API responses and trace
  --json, -j                     JSON output mode (machine-readable)
  --check-availability, -c       Check if recording is ready
  --recording-id TEXT            Select specific recording by UUID
  --wait MINUTES                 Wait for recording processing (timeout)
  --skip-transcript              Skip transcript download
  --skip-chat                    Skip chat log download
  --skip-timeline                Skip timeline download
  --dry-run                      Show what would be downloaded
  --log-file PATH                Write structured log (JSONL format)
  --config PATH                  Path to config file (JSON/YAML)
  --filename-template TEXT       Custom filename template
  --folder-template TEXT         Custom folder structure template
  --help                         Show this message and exit
  --version                      Show version and exit

Recordings Options

dlzoom recordings [OPTIONS]

User-wide mode (default):
  --from-date TEXT               Start date (YYYY-MM-DD)
  --to-date TEXT                 End date (YYYY-MM-DD)
  --range [today|yesterday|last-7-days|last-30-days]
                                 Quick date range (exclusive with --from-date/--to-date)
  --topic TEXT                   Substring filter on topic
  --limit INTEGER                Max results (0 = unlimited) [default: 1000]
  --page-size INTEGER            [Advanced] Results per API request (Zoom max 300) [default: 300]

Meeting-scoped mode (replaces `download --list`):
  --meeting-id TEXT              Exact meeting ID or UUID to list instances

Common options:
  --json, -j                     JSON output mode (silent)
  --verbose, -v                  Verbose human output
  --debug, -d                    Debug logging
  --config PATH                  Path to config file
  --help                         Show this message and exit

Template Variables

Use in --filename-template and --folder-template:

  • {topic} - Meeting topic
  • {meeting_id} - Meeting ID
  • {host_email} - Host email address
  • {start_time:%Y%m%d} - Start time (format with strftime codes)
  • {duration} - Meeting duration

Examples:

# Date-based filename
--filename-template "{start_time:%Y%m%d}_{topic}"

# Organized by date
--folder-template "{start_time:%Y}/{start_time:%m}"

# Include host
--filename-template "{host_email}_{topic}_{start_time:%Y%m%d}"

Common Use Cases

Download Latest Recording from Recurring Meeting

dlzoom download 123456789 --verbose

Download Specific Instance

# List all instances first (meeting-scoped view)
dlzoom recordings --meeting-id 123456789

# Download a specific instance by UUID
dlzoom download 123456789 --recording-id "abc123def456"

Automated Pipeline (JSON Output)

dlzoom download 123456789 --json > recording.json

Batch Processing

#!/bin/bash
for meeting_id in 111111111 222222222 333333333; do
    dlzoom download $meeting_id --output-dir ./recordings
done

Wait for Recording to Process

# Wait up to 60 minutes for processing
dlzoom download 123456789 --wait 60

Troubleshooting

Authentication Failed

Error: Authentication failed

Solution: Check your credentials in .env or environment variables.

Meeting ID Format Invalid

Error: Invalid meeting ID format

Solution: Meeting IDs must be:

  • 9-12 digit numbers (e.g., 123456789)
  • Or UUID format (e.g., abc123XYZ+/=_-)

Recording Not Found

Error: Recording not found

Possible causes:

  • Meeting wasn't recorded
  • Recording not yet processed (use --wait)
  • No permission to access recording
  • Wrong meeting ID

Check availability first:

dlzoom download 123456789 --check-availability

ffmpeg Not Found

Error: ffmpeg not found in PATH

Solution: Install ffmpeg:

# macOS
brew install ffmpeg

# Ubuntu/Debian
sudo apt install ffmpeg

Rate Limit Exceeded

Error: Rate limit exceeded

Solution: Wait a few minutes and try again. The tool automatically retries with exponential backoff.

Insufficient Disk Space

Error: Insufficient disk space

Solution: Free up space or use --output-dir to save to a different location.

Configuration Files

JSON Config

{
  "zoom_account_id": "your_account_id",
  "zoom_client_id": "your_client_id",
  "zoom_client_secret": "your_client_secret",
  "output_dir": "./recordings",
  "log_level": "INFO"
}

YAML Config

zoom_account_id: "your_account_id"
zoom_client_id: "your_client_id"
zoom_client_secret: "your_client_secret"
output_dir: "./recordings"
log_level: "INFO"

Use with:

dlzoom download 123456789 --config config.yaml

Output Files

Audio file:

  • Format: M4A (AAC audio)
  • Naming: {meeting_id}.m4a or custom via --output-name

Transcript file:

  • Format: VTT (WebVTT)
  • Naming: {meeting_id}_transcript.vtt

Chat log:

  • Format: TXT
  • Naming: {meeting_id}_chat.txt

Timeline:

  • Format: JSON
  • Naming: {meeting_id}_timeline.json
  • Contains: Meeting events (joins, leaves, screen shares, etc.)

Metadata:

  • Format: JSON
  • Naming: {meeting_id}_metadata.json
  • Contains: Meeting info, participants, recording details

Requirements

  • Python 3.11 or higher
  • ffmpeg (for audio extraction)
  • Zoom account (User OAuth via dlzoom login) or S2S OAuth app (optional)

Security note (tokens): On Windows, file permission enforcement for tokens.json is best‑effort. Treat your token file as sensitive and ensure your user profile is protected.

Broker Origin Restriction (Optional)

For tighter security on the hosted auth service, you can restrict which origin is allowed to call the token endpoints:

  • Set the ALLOWED_ORIGIN environment variable on your Cloudflare Worker (e.g., your CLI’s origin or a specific domain). When set, the broker sends Access-Control-Allow-Origin: <value> and Vary: Origin instead of *.
  • See zoom-broker/README.md for details. If you don’t set it, the broker defaults to * — acceptable for CLI usage but less restrictive.

Development

# Clone repository
git clone <repo-url>
cd dlzoom

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # or `.venv\Scripts\activate` on Windows

# Install in development mode
pip install -e .

# Run tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=src/dlzoom --cov-report=term-missing

Version

See CHANGELOG.md for detailed release notes. Current version: 0.2.0.

Roadmap

Planned for future releases:

  • 🎨 More output formats (TSV)
  • 🔐 Token encryption via system keychain
  • 📱 Multiple profiles support
  • 📦 Optional SBOM generation in CI

Known Limitations

Feature Limitations

  • Audio quality parameter not exposed via CLI (internal only)

License

MIT License - see LICENSE file for details.

Contributing

We welcome contributions! Here's how you can help:

Quick Start for Contributors

  1. Fork and clone the repository

  2. Set up development environment:

    python3.11 -m venv .venv
    source .venv/bin/activate
    pip install -e ".[dev]"
    
  3. Run tests:

    pytest tests/ -v
    
  4. Make your changes and ensure tests pass

  5. Submit a pull request

Guidelines

  • Follow Conventional Commits for commit messages
  • Add tests for new features
  • Update documentation as needed
  • Ensure all CI checks pass

Commit Message Format

<type>(<scope>): <subject>

Examples:
feat(cli): add support for CSV export
fix(auth): handle expired OAuth tokens
docs: update installation instructions

Types:

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation
  • test - Tests
  • refactor - Code refactoring
  • chore - Maintenance

For detailed guidelines, see CONTRIBUTING.md.

Code of Conduct

Security

Security is important to us. If you discover a security vulnerability:

Security Features

  • ✅ Credentials never logged or exposed
  • ✅ Input validation (prevents injection attacks)
  • ✅ Atomic file operations
  • ✅ Automatic security scanning (Trivy)
  • ✅ Docker images run as non-root user

Support

For issues and questions:

Credits

Built with:

Acknowledgments

Thanks to all contributors and the open source community.

Architecture

This is a monorepo containing two independently deployable components:

Repository Structure

dlzoom/
├── src/dlzoom/           # Python CLI (primary deliverable)
├── tests/                # Python test suite
├── zoom-broker/          # Cloudflare Worker (OAuth broker)
├── docs/                 # Documentation
│   ├── architecture.md   # Detailed architecture overview
│   └── zoom-app/         # Zoom App marketplace docs
└── pyproject.toml        # Python package configuration

Components:

  • Python CLI (src/dlzoom/): Command-line tool to browse and download Zoom cloud recordings. Published to PyPI as dlzoom.
  • OAuth Broker (zoom-broker/): Cloudflare Worker that performs OAuth code exchange and token refresh on behalf of the CLI. Optional component for user OAuth mode.

Why a monorepo?

  • Simplified development workflow (single repo to clone, single issue tracker)
  • Shared documentation and versioning strategy
  • Python CLI is the primary deliverable; broker is a supporting service
  • Each component remains independently deployable

📖 See docs/architecture.md for detailed architecture, data flows, security model, and deployment options.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dlzoom-0.2.0.tar.gz (67.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dlzoom-0.2.0-py3-none-any.whl (54.1 kB view details)

Uploaded Python 3

File details

Details for the file dlzoom-0.2.0.tar.gz.

File metadata

  • Download URL: dlzoom-0.2.0.tar.gz
  • Upload date:
  • Size: 67.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dlzoom-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c15c020c276d1b33b77efa000c7ad28341dd82e9710c1e48035c80e382197f9f
MD5 fdf1bee4106dfb3467b0767525e2ce39
BLAKE2b-256 86cf9d5cdb57e4a6b567a96a081cbae352d0b24aac8d8f673c7309ec52e464e2

See more details on using hashes here.

Provenance

The following attestation bundles were made for dlzoom-0.2.0.tar.gz:

Publisher: release.yml on yaniv-golan/dlzoom

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dlzoom-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: dlzoom-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 54.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dlzoom-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f7b2abf05240c273d8a75171adf3a44fccf84817fa4010073135d4a4507d3c45
MD5 cc0d3eb35e3db893e88cbb1593ef5d7d
BLAKE2b-256 9fa9e68f75fe731548ac98fd5d9ef89e1d899ed88d83902bc406852350c22687

See more details on using hashes here.

Provenance

The following attestation bundles were made for dlzoom-0.2.0-py3-none-any.whl:

Publisher: release.yml on yaniv-golan/dlzoom

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page