Skip to content

Commit

Permalink
feat: initial commit working next.js demo
Browse files Browse the repository at this point in the history
  • Loading branch information
lukeocodes committed Nov 6, 2023
1 parent bca937c commit a3faf0f
Show file tree
Hide file tree
Showing 18 changed files with 3,164 additions and 116 deletions.
75 changes: 55 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,71 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
# Live audio Next.js Starter

## Getting Started
This sample demonstrates interacting with Deepgram from Next.js to transcribe your microphone audio. It uses the Deepgram JavaScript SDK. This was originally a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

First, run the development server:
## Sign-up to Deepgram

Before you start, it's essential to generate a Deepgram API key to use in this project. [Sign-up now for Deepgram](https://console.deepgram.com/signup).

## Quickstart

### Manual

Follow these steps to get started with this starter application.

#### Clone the repository

Go to GitHub and [clone the repository](https://github.com/deepgram-starters/live-nextjs-starter).

#### Install dependencies

Install the project dependencies.

```bash
npm install
```

#### Edit the config file

Copy the code from `sample.env.local` and create a new file called `.env.local`. Paste in the code and enter your API key you generated in the [Deepgram console](https://console.deepgram.com/).

```bash
DEEPGRAM_API_KEY=%api_key%
```

#### Run the application

Once running, you can [access the application in your browser](http://localhost:3000).

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## What is Deepgram?

Deepgram is an AI speech platform which specializes in (NLU) Natural Language Understanding features and Transcription. It can help get the following from your audio.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
- [Speaker diarization](https://deepgram.com/product/speech-understanding/)
- [Language detection](https://deepgram.com/product/speech-understanding/)
- [Summarization](https://deepgram.com/product/speech-understanding/)
- [Topic detection](https://deepgram.com/product/speech-understanding/)
- [Language translation](https://deepgram.com/product/speech-understanding/)
- [Sentiment analysis](https://deepgram.com/product/speech-understanding/)
- [Entity detection](https://deepgram.com/product/speech-understanding/)
- [Transcription](https://deepgram.com/product/transcription/)
- [Redaction](https://deepgram.com/product/transcription/)

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Create a Free Deepgram Account

## Learn More
Before you start, it's essential to generate a Deepgram API key to use in our starter applications. [Sign-up now for Deepgram](https://console.deepgram.com/signup).

To learn more about Next.js, take a look at the following resources:
## Issue Reporting

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Security Policy](./SECURITY.md) details the procedure for contacting Deepgram.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Author

## Deploy on Vercel
[Deepgram](https://deepgram.com)

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
## License

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
This project is licensed under the MIT license. See the [LICENSE](./LICENSE) file for more info.
39 changes: 39 additions & 0 deletions app/api/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { DeepgramError, createClient } from "@deepgram/sdk";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
// gotta use the request object to invalidate the cache every request :vomit:
const url = request.url;
const deepgram = createClient(process.env.DEEPGRAM_API_KEY ?? "");

let { result: projectsResult, error: projectsError } =
await deepgram.manage.getProjects();

if (projectsError) {
return NextResponse.json(projectsError);
}

const project = projectsResult?.projects[0];

if (!project) {
return NextResponse.json(
new DeepgramError(
"Cannot find a Deepgram project. Please create a project first."
)
);
}

let { result: newKeyResult, error: newKeyError } =
await deepgram.manage.createProjectKey(project.project_id, {
comment: "Temporary API key",
scopes: ["usage:write"],
tags: ["next.js"],
time_to_live_in_seconds: 10,
});

if (newKeyError) {
return NextResponse.json(newKeyError);
}

return NextResponse.json({ ...newKeyResult, url });
}
14 changes: 14 additions & 0 deletions app/captions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use client";

import { useTranscriptionContext } from "@/context/transcription";
import { useState } from "react";

export default function Captions() {
const { transcription } = useTranscriptionContext();

return (
<div className="captions" id="captions">
<span>{transcription ?? "Captions by Deepgram"}</span>
</div>
);
}
5 changes: 5 additions & 0 deletions app/dg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
180 changes: 180 additions & 0 deletions app/microphone.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"use client";

import {
CreateProjectKeyResponse,
LiveClient,
LiveTranscriptionEvents,
createClient,
} from "@deepgram/sdk";
import { useState, useEffect, useCallback } from "react";
import { useQueue } from "@uidotdev/usehooks";
import Dg from "./dg.svg";
import Recording from "./recording.svg";
import Image from "next/image";

export default function Microphone() {
const { add, remove, first, size, queue } = useQueue<any>([]);
const [apiKey, setApiKey] = useState<CreateProjectKeyResponse | null>();
const [connection, setConnection] = useState<LiveClient | null>();
const [isListening, setListening] = useState(false);
const [isLoadingKey, setLoadingKey] = useState(true);
const [isLoading, setLoading] = useState(true);
const [isProcessing, setProcessing] = useState(false);
const [micOpen, setMicOpen] = useState(false);
const [microphone, setMicrophone] = useState<MediaRecorder | null>();
const [userMedia, setUserMedia] = useState<MediaStream | null>();
const [caption, setCaption] = useState<string | null>();

const toggleMicrophone = useCallback(async () => {
if (microphone && userMedia) {
setUserMedia(null);
setMicrophone(null);

microphone.stop();
} else {
const userMedia = await navigator.mediaDevices.getUserMedia({
audio: true,
});

const microphone = new MediaRecorder(userMedia);
microphone.start(500);

microphone.onstart = () => {
setMicOpen(true);
};

microphone.onstop = () => {
setMicOpen(false);
};

microphone.ondataavailable = (e) => {
add(e.data);
};

setUserMedia(userMedia);
setMicrophone(microphone);
}
}, [add, microphone, userMedia]);

useEffect(() => {
if (!apiKey) {
console.log("getting a new api key");
fetch("/api", { cache: "no-store" })
.then((res) => res.json())
.then((object) => {
if (!("key" in object)) throw new Error("No api key returned");

setApiKey(object);
setLoadingKey(false);
})
.catch((e) => {
console.error(e);
});
}
}, [apiKey]);

useEffect(() => {
if (apiKey && "key" in apiKey) {
console.log("connecting to deepgram");
const deepgram = createClient(apiKey?.key ?? "");
const connection = deepgram.listen.live({
model: "nova",
interim_results: true,
});

connection.on(LiveTranscriptionEvents.Open, () => {
console.log("connection established");
setListening(true);
});

connection.on(LiveTranscriptionEvents.Close, () => {
console.log("connection closed");
setListening(false);
setApiKey(null);
setConnection(null);
});

connection.on(LiveTranscriptionEvents.Transcript, (data) => {
const words = data.channel.alternatives[0].words;
const caption = words
.map((word: any) => word.punctuated_word ?? word.word)
.join(" ");
if (caption !== "") {
setCaption(caption);
}
});

setConnection(connection);
setLoading(false);
}
}, [apiKey]);

useEffect(() => {
const processQueue = async () => {
if (size > 0 && !isProcessing) {
setProcessing(true);

if (isListening) {
const blob = first;
connection?.send(blob);
remove();
}

const waiting = setTimeout(() => {
clearTimeout(waiting);
setProcessing(false);
}, 250);
}
};

processQueue();
}, [connection, queue, remove, first, size, isProcessing, isListening]);

if (isLoadingKey) return "Loading temporary API key...";
if (isLoading) return "Loading the app...";

return (
<div className="w-full relative">
<div className="mt-10 flex flex-col align-middle items-center">
<Image
src="/click.png"
width="168"
height="129"
alt="Deepgram Logo"
priority
/>
<button className="w-24 h-24" onClick={() => toggleMicrophone()}>
<Recording
width="96"
height="96"
className={
`cursor-pointer` + !!userMedia && !!microphone && micOpen
? "fill-red-400 drop-shadow-glowRed"
: "fill-gray-600"
}
/>
</button>
<div className="mt-20 uppercase p-6 text-xl text-center">
{caption ?? "Captions by Deepgram"}
</div>
</div>
<div
className="z-20 text-white flex shrink-0 grow-0 justify-around items-center
fixed bottom-0 right-5 rounded-lg mr-1 mb-5 lg:mr-5 lg:mb-5 xl:mr-10 xl:mb-10 gap-5"
>
<span className="text-sm text-gray-400">
{isListening
? "Deepgram connection open!"
: "Deepgram is connecting..."}
</span>
<Dg
width="30"
height="30"
className={
isListening ? "fill-white drop-shadow-glowBlue" : "fill-gray-600"
}
/>
</div>
</div>
);
}
Loading

0 comments on commit a3faf0f

Please sign in to comment.