DEV Community

German Escobar
German Escobar

Posted on

Zoom recording to S3 using an AWS Lambda

I was using Pipedream to store my Zoom recordings in S3 but they changed the pricing and it is not free anymore. So, I decided that this would be a fun project in any case.

ChatGPT wasn't very helpful, eventually this repo helped me. Here's the final code:

import { S3Client } from '@aws-sdk/client-s3';
import crypto from 'crypto';
import { nanoid } from 'nanoid';
import { Upload } from '@aws-sdk/lib-storage';
import got from 'got';
import { format } from 'date-fns';

const zoomSecretToken = process.env.ZOOM_SECRET_TOKEN;
const s3 = new S3Client({ region: "us-east-1" });

const bucketName = "...";

export const handler = async (event) => {
  try {
    const headers = event.headers;
    const zoomEvent = typeof event.body === 'string' ? JSON.parse(event.body) : event.body;

    if (zoomEvent.event === 'endpoint.url_validation') {
      const encryptedToken = crypto.createHmac('sha256', zoomSecretToken).update(zoomEvent.payload.plainToken).digest('hex');
      return {
        statusCode: 200,
        body: JSON.stringify({ plainToken: zoomEvent.payload.plainToken, encryptedToken })
      }
    } else if (zoomEvent.event !== 'recording.completed') {
      return { statusCode: 422, body: 'Invalid event type' };
    }

    const meetingId = zoomEvent.payload.object.id;
    const datePath = format(new Date(), 'yyyy-MM-dd');

    // Retrieve recording URLs from the event payload
    const recordingFiles = zoomEvent.payload.object.recording_files;

    const videoRecordings = recordingFiles.filter(file => file.file_type === "MP4" && !file.recording_type.includes("audio"));
    if (videoRecordings.length === 0) {
      return { statusCode: 409, body: 'No video recordings found' };
    }

    const file = videoRecordings[0];
    const fileUrl = file.download_url + "?access_token=" + zoomEvent.download_token;
    const filepath = `videos/${meetingId}/${datePath}/${nanoid(10)}.${file.file_type.toLowerCase()}`;

    // Download the file and upload to S3
    await uploadToS3(fileUrl, bucketName, filepath);

    return {
      statusCode: 200,
      body: 'Zoom recording successfully uploaded to S3',
    };
  } catch (error) {
    return {
      statusCode: 500,
      body: JSON.stringify({ message: 'Error uploading Zoom recording', error: error.message }),
    };
  }
};

// Helper function to upload file from URL to S3
const uploadToS3 = async (fileUrl, bucketName, key) => {
  const stream = got.stream(fileUrl);

  const upload = new Upload({
    client: s3,
    params: {
      Bucket: bucketName,
      Key: key,
      Body: stream,
      ContentType: 'video/mp4',
      ACL: 'public-read',
    },
  });

  const result = await upload.done();
  return result;
};
Enter fullscreen mode Exit fullscreen mode

My package.json:

{
  "name": "zoom-to-s3",
  "version": "1.0.0",
  "description": "",
  "main": "index.mjs",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "zip": "zip -r function.zip ."
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@aws-sdk/client-lambda": "^3.659.0",
    "date-fns": "^4.1.0",
    "got": "^14.4.2",
    "nanoid": "^5.0.7",
    "postmark": "^4.0.5"
  }
}
Enter fullscreen mode Exit fullscreen mode

And my .github/workflows/deploy.yml to deploy it when I push to main:

name: Deploy Lambda

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'

      - name: Install Dependencies
        run: npm install

      - name: Zip Lambda Function
        run: npm run zip

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Deploy Lambda
        run: |
          aws lambda update-function-code \
            --function-name zoom-recording-to-s3 \
            --zip-file fileb://function.zip

Enter fullscreen mode Exit fullscreen mode

Image of AssemblyAI tool

Transforming Interviews into Publishable Stories with AssemblyAI

Insightview is a modern web application that streamlines the interview workflow for journalists. By leveraging AssemblyAI's LeMUR and Universal-2 technology, it transforms raw interview recordings into structured, actionable content, dramatically reducing the time from recording to publication.

Key Features:
🎥 Audio/video file upload with real-time preview
🗣️ Advanced transcription with speaker identification
⭐ Automatic highlight extraction of key moments
✍️ AI-powered article draft generation
📤 Export interview's subtitles in VTT format

Read full post

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay