May 10, 2024
How to Create a YouTube Video Idea Generator with AI using NextJS
In this tutorial, we are going to walk through how to create a full fledge NextJS app that can generate YouTube Video ideas using AI.
We will be using NextJS for a frontend and backend along with Typescript.
For our AI, we will show how you can use OpenAI (GPT) or Claude AI
While the app we will build will focus just on generating YouTube video ideas, the same app can be used to generate anything with AI.
The full source code is available here.
Let's get started...
Step 1 - Create NextJS project
First thing we need to do is create a NextJS app.
If you don't already have NodeJS installed, you can install it here.
Once you have NodeJS installed, create a directory somewhere on your local machine and navigate in that directory and then initialize a NextJS app using the following commands.
cd ./youtube-idea-generator
npx create-next-app@latest
Name your project something like `youtube-idea-generator` and then just accept all the defaults like this...
If you run into any issues, NextJS has great documentation and they walk through creating an app here.
Step 2 - Install Dependencies
Next, we need to install our dependencies which include the following...
...along with a few other dependencies that will help get information from YouTube.
You can install all of the dependencies using the following commands...
npm install @anthropic-ai/sdk
npm install openai
npm install youtubei.js
npm install @gonetone/get-youtube-id-by-url
npm install yt-channel-info
Step 3 - Build the Frontend
Now we can build our user interface which will initially consist of a textbox and a button.
Paste the following code in /app/Page.tsx
"use client"
import LoadingButton from "@mui/lab/LoadingButton";
import { Box, Container, Stack, TextField, Typography } from "@mui/material";
import { useState } from "react";
export default function Page() {
const [channelUrl, setChannelUrl] = useState("")
const [isLoading, setIsLoading] = useState(false)
const onGenerateSuggestions = async () => {
setIsLoading(true)
try {
// TODO: get suggestions
}
catch (e) {
// Something went wrong - show an error
}
finally {
setIsLoading(false)
}
}
const onChannelUrlChange = (e: any) => {
setChannelUrl(e.target.value)
}
return (
<>
<Container maxWidth='md' className='blog' sx={{ pb: 8 }}>
<Box textAlign={'center'}>
<Typography variant="h1">YouTube Video Ideas Generator</Typography>
<Typography variant="h6">Get ideas for your next video based on your previous videos</Typography>
</Box>
<Stack spacing={2} sx={{ mt: 5 }}>
<TextField
sx={{ mt: 1, mb: 2 }}
value={channelUrl}
label='YouTube Channel URL'
placeholder={'https://www.youtube.com/@channel'}
fullWidth
variant="outlined"
onChange={onChannelUrlChange}
/>
<Box textAlign={'center'}>
<Box display='flex' justifyContent={'center'} mt={3}>
<LoadingButton
sx={{ minHeight: '48px', fontSize: '18px', borderRadius: '80px', pl: 4, pr: 4 }}
onClick={onGenerateSuggestions}
variant='contained'
loading={isLoading} >
Generate Suggestions
</LoadingButton>
</Box>
</Box>
</Stack>
</Container>
</>
)
}
You can now run your app using npm run dev.
Once you run your app you can navigate to http://localhost:3000 in your browser and you should see something like this...
Step 4 - Build the Backend
Next step is to build the backend where we take in a YouTube channel URL and use that to extract information about the channel and feed that information to our AI to generate suggestions for our next video.
Create a file in the /app directory called /api/route.ts and then paste the following code in the file...
import { NextResponse } from "next/server";
import ytch from 'yt-channel-info'
//@ts-ignore
import getYtId from '@gonetone/get-youtube-id-by-url'
import { Innertube } from 'youtubei.js';
import OpenAI from "openai";
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic({
apiKey: 'YOUR API KEY',
});
const openAi = new OpenAI({ apiKey: 'YOUR API KEY' })
export async function POST(request: Request) {
const { channelUrl } = await request.json()
const youtube = await Innertube.create();
try {
const payload = {
channelId: await getYtId.channelId(channelUrl),
sortBy: 'newest' as "newest" | "oldest" | "popular" | undefined,
channelIdType: 0
}
const [channel, channelVideos] = await Promise.all([
youtube.getChannel(payload.channelId),
ytch.getChannelVideos(payload),
])
const channelDescription = (await channel.getAbout() as any).metadata?.description
const videoInfos = await Promise.all(channelVideos.items.map(video => {
return youtube.getBasicInfo(video.videoId)
}))
const prompt = `
Come up with 3 ideas for my next YouTube video for my YouTube channel given my channel description and previous videos.
For each idea, give me a video title and description.
Output your response in JSON. Do not include a preamble or any other text other than JSON.
<channel-description>
"${channelDescription}"
</channel-description>
<previous-videos>
${videoInfos.map(videoInfo => {
return `
<previous-video>
<title>
${videoInfo.basic_info.title}
</title>
<description>
${videoInfo.basic_info.short_description?.replaceAll('\n', '\n ')}
</description>
</previous-video>
`
}).join('\n')}
</previous-videos>
<output-format>
Output your response in JSON in the following format...
[
{
"title": <string>,
"description": <string>
},
...
]
</output-format>
`
const result = await anthropic.messages.create({
model: 'claude-3-haiku-20240307',
temperature: 0.8,
max_tokens: 4000,
messages: [
{
role: 'user',
content: prompt
},
],
});
const output = result.content[0].text;
const suggestions = JSON.parse(output ?? "")
//
// Uncomment below if you want to use OpenAI instead of Claude
//
// const result = await openAi.chat.completions.create({
// model: "gpt-3.5-turbo-1106",
// temperature: 0.8,
// messages: [{
// role: "user",
// content: prompt
// }],
// });
// const output = result.choices[0]?.message.content?.replace('```json', '').replace('```', '')
return NextResponse.json({ suggestions }, { status: 200 });
}
catch (e: any) {
console.error("Error", e)
return NextResponse.json({}, { status: 500 })
}
}
Be sure to paste in your API keys for either OpenAI or Claude.
There's a lot going on with that code so I will explain as best as I can, but basically this code creates an endpoint that does the following...
- Parses the NextJS request to get a channel URL param
- Uses the channel URL param to get a channel ID
- Uses the channel ID to get the channel information and get recent videos
- Uses the channel information and videos to create an AI prompt
- Sends the prompt to AI using OpenAI or Claude
- Waits for the AI response and parses into JSON
- Returns the result
Step 5 - Wire up the Backend and Render the result
Now that our endpoint is created we can call the endpoint on the frontend and display the results.
Paste the following code into /app/Page.tsx which just expands upon the previous code we had...
"use client"
import LoadingButton from "@mui/lab/LoadingButton";
import { Box, Card, CardHeader, Container, Stack, TextField, Typography } from "@mui/material";
import { useState } from "react";
export default function Page() {
const [channelUrl, setChannelUrl] = useState("")
const [suggestions, setSuggestions] = useState<any[]>([])
const [isLoading, setIsLoading] = useState(false)
const onGenerateSuggestions = async () => {
setIsLoading(true)
try {
const response = await fetch('/api', {
method: 'POST',
body: JSON.stringify({
channelUrl,
})
})
const json = await response.json()
setSuggestions(json.suggestions)
}
catch (e) {
// Something went wrong - show an error
console.error(e)
}
finally {
setIsLoading(false)
}
}
const onChannelUrlChange = (e: any) => {
setChannelUrl(e.target.value)
}
return (
<>
<Container maxWidth='md' className='blog' sx={{ pb: 8 }}>
<Box textAlign={'center'}>
<Typography variant="h1">YouTube Video Ideas Generator</Typography>
<Typography variant="h6">Get ideas for your next video based on your previous videos</Typography>
</Box>
<Stack spacing={2} sx={{ mt: 5 }}>
<TextField
sx={{ mt: 1, mb: 2 }}
value={channelUrl}
label='YouTube Channel URL'
placeholder={'https://www.youtube.com/@channel'}
fullWidth
variant="outlined"
onChange={onChannelUrlChange}
/>
<Box textAlign={'center'}>
<Box display='flex' justifyContent={'center'} mt={3}>
<LoadingButton
sx={{ minHeight: '48px', fontSize: '18px', borderRadius: '80px', pl: 4, pr: 4 }}
onClick={onGenerateSuggestions}
variant='contained'
loading={isLoading} >
Generate Suggestions
</LoadingButton>
</Box>
</Box>
</Stack>
{!!suggestions.length && <Stack sx={{ pt: 5 }}>
<Typography variant="h5" textAlign={'center'}>Here are your suggestions... </Typography>
<Stack spacing={3}>
{suggestions.map(suggestion => {
return (
<Card variant="outlined" key={suggestion.title}>
<CardHeader
title={suggestion.title}
subheader={suggestion.description}
/>
</Card>
)
})}
</Stack>
</Stack>
}
</Container>
</>
)
}
The previous code simply calls the API endpoint we just added and then renders the result.
Once you have updated the code you can try pasting in a channel URL in the box and clicking the button and seeing the results which should look something like this...
Conclusion
There you go! You just successfully created a full app that uses AI to generate YouTube suggestions using just a channel URL.
You can easily modify this app to use AI for anything really.
If you want to see a more built out demo of this, you can check it out part of our free tools for YouTube creators.
Also, the full source code for the example project is GitHub.