AINode.jsAutomation
AI YouTube Video Summarizer
TT
TopicTrick Team
AI YouTube Video Summarizer
Paste a YouTube URL and receive a structured summary with chapters, key insights, and action items — in seconds. This tool fetches the video transcript automatically and uses GPT-4o to condense hours of content into a focused, readable brief.
This is Tool 29 of the Build 50 AI Automation Tools course.
What You'll Build
POST /summarize— YouTube URL in, structured summary out- Auto-fetch transcript (no API key needed for most videos)
- Chapters, key insights, quotes, and action items
- Map-reduce for long videos (>1 hour)
Setup
bash
mkdir yt-summarizer && cd yt-summarizer
npm init -y
npm install express youtube-transcript openai p-limit dotenvbash
# .env
OPENAI_API_KEY=sk-your-key-here
PORT=3000Transcript & Summary Service
js
// src/services/summaryService.js
import { YoutubeTranscript } from 'youtube-transcript';
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
function extractVideoId(url) {
const patterns = [
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
/youtube\.com\/shorts\/([^&\n?#]+)/,
];
for (const pattern of patterns) {
const match = url.match(pattern);
if (match) return match[1];
}
throw new Error('Could not extract video ID from URL');
}
async function fetchTranscript(videoId) {
const items = await YoutubeTranscript.fetchTranscript(videoId);
return items.map(item => ({
text: item.text,
offset: item.offset,
duration: item.duration,
}));
}
function transcriptToText(items) {
return items.map(i => i.text).join(' ').replace(/\s+/g, ' ').trim();
}
function chunkTranscript(items, chunkDurationMs = 10 * 60 * 1000) {
const chunks = [];
let current = [];
let chunkStart = 0;
for (const item of items) {
if (item.offset - chunkStart > chunkDurationMs && current.length > 0) {
chunks.push({ text: current.join(' '), startMs: chunkStart, endMs: item.offset });
current = [];
chunkStart = item.offset;
}
current.push(item.text);
}
if (current.length > 0) {
chunks.push({ text: current.join(' '), startMs: chunkStart, endMs: Infinity });
}
return chunks;
}
async function summarizeTranscript(text, title = '') {
const truncated = text.length > 60_000 ? text.slice(0, 60_000) + ' [truncated]' : text;
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `You are an expert content summarizer. Analyze this YouTube video transcript and provide a structured summary.
Return ONLY a JSON object:
{
"title": "video title or inferred topic",
"oneSentenceSummary": "one sentence that captures the entire video",
"summary": "3-4 paragraph comprehensive summary",
"chapters": [
{ "title": "chapter title", "summary": "2-3 sentence chapter summary", "timestamp": "estimated MM:SS" }
],
"keyInsights": ["5-7 most important insights or takeaways"],
"quotes": ["2-3 most quotable or memorable lines from the transcript"],
"actionItems": ["concrete things a viewer can do after watching"],
"targetAudience": "who this video is best suited for",
"difficulty": "beginner | intermediate | advanced",
"topics": ["main topics covered"]
}`,
},
{
role: 'user',
content: `Video title: ${title}\n\nTranscript:\n${truncated}`,
},
],
temperature: 0.3,
response_format: { type: 'json_object' },
});
return JSON.parse(response.choices[0].message.content);
}
async function mapReduceSummarize(chunks, title) {
// Map: summarize each chunk
const chunkSummaries = await Promise.all(
chunks.map(async (chunk, i) => {
const resp = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: 'Summarize this transcript segment in 3-5 sentences. Be concise.' },
{ role: 'user', content: chunk.text.slice(0, 8_000) },
],
temperature: 0.2,
});
return `Segment ${i + 1}: ${resp.choices[0].message.content}`;
})
);
// Reduce: synthesize chunk summaries
const combined = chunkSummaries.join('\n\n');
return summarizeTranscript(combined, title);
}
export async function summarizeVideo(url) {
const videoId = extractVideoId(url);
const transcriptItems = await fetchTranscript(videoId);
const fullText = transcriptToText(transcriptItems);
// Long video: use map-reduce
const totalDurationMs = transcriptItems[transcriptItems.length - 1]?.offset || 0;
const ONE_HOUR = 60 * 60 * 1000;
let summary;
if (totalDurationMs > ONE_HOUR) {
const chunks = chunkTranscript(transcriptItems);
summary = await mapReduceSummarize(chunks, '');
} else {
summary = await summarizeTranscript(fullText);
}
return {
videoId,
url,
transcriptLength: transcriptItems.length,
durationMinutes: Math.round(totalDurationMs / 60_000),
...summary,
};
}Server
js
// src/server.js
import 'dotenv/config';
import express from 'express';
import { summarizeVideo } from './services/summaryService.js';
const app = express();
app.use(express.json());
app.post('/summarize', async (req, res, next) => {
try {
const { url } = req.body;
if (!url) return res.status(400).json({ error: 'YouTube URL required' });
const summary = await summarizeVideo(url);
res.json({ success: true, ...summary });
} catch (err) { next(err); }
});
app.use((err, _req, res, _next) => res.status(500).json({ error: err.message }));
app.listen(process.env.PORT ?? 3000, () => console.log('YouTube summarizer running'));Testing
bash
curl -X POST http://localhost:3000/summarize \
-H "Content-Type: application/json" \
-d '{ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }'Sample response:
json
{
"videoId": "dQw4w9WgXcQ",
"durationMinutes": 3,
"oneSentenceSummary": "A timeless pop anthem about perseverance and emotional vulnerability in relationships.",
"keyInsights": [
"Persistence and emotional honesty are central themes",
"The chorus emphasises commitment despite uncertainty"
],
"actionItems": [
"Reflect on how you communicate commitment in your own relationships"
],
"difficulty": "beginner",
"topics": ["music", "relationships", "pop culture"]
}Build 50 AI Automation Tools — Tool 29 of 50
YouTube summarizer is live. Continue to Tool 30 to build an AI podcast transcript summarizer.
Summary
- youtube-transcript fetches captions without an API key — works for most English videos with auto-captions
- Map-reduce pattern handles hour-long lectures and conference talks without truncation
- gpt-4o-mini for chunk summaries cuts cost significantly — only the final synthesis uses GPT-4o
- Chapters with timestamps make the summary navigable — readers can jump to specific sections
- Extend with a
/searchendpoint that searches across a library of summarized videos by topic
Continue to Tool 30: AI Podcast Transcript Summarizer →
