在Next.js项目中使用youtube-transcript获取字幕报错的解决咨询
问题说明
单个JS/TS文件中使用youtube-transcript包获取YouTube视频字幕完全正常,但在Next.js项目中运行时出现报错:
Uncaught (in promise) Error: [YoutubeTranscript] 🚨 TypeError: URL is not a constructor
at new YoutubeTranscriptError (youtube-transcript.esm.js:74:1)
at Function.eval (youtube-transcript.esm.js:134:1)
at step (youtube-transcript.esm.js:64:1)
at Object.eval [as throw] (youtube-transcript.esm.js:45:46)
at rejected (youtube-transcript.esm.js:36:42)
单个文件运行的预期结果:
[ { text: 'what is going on everyone my name is', duration: 2800, offset: 399 }, { text: "anson and in this video i'm going to go", duration: 2960, offset: 1680 }, // ... 更多字幕条目 ]
原因分析
youtube-transcript包内部依赖Node.js环境的URL构造函数(或node:url模块),而Next.js客户端组件运行在浏览器环境中,包的代码未做浏览器兼容处理,导致出现URL is not a constructor错误。单个JS/TS文件运行在Node.js环境,因此无此问题。
解决方案
方案一:使用Next.js服务器组件运行代码
服务器组件运行在Node.js环境,与单个JS文件的运行环境一致,可直接调用包:
// app/TranscriptServerComponent.tsx import { YoutubeTranscript } from "youtube-transcript"; export default async function TranscriptServerComponent() { const transcript = await YoutubeTranscript.fetchTranscript("QY8dhl1EQfI"); return ( <div> <h3>视频字幕</h3> <pre>{JSON.stringify(transcript, null, 2)}</pre> </div> ); }
方案二:创建API路由,客户端通过接口调用
如果需要在客户端组件中使用字幕数据,可创建Next.js API路由处理字幕获取逻辑,客户端通过fetch调用该接口:
1. 创建API路由
// app/api/transcript/route.ts (App Router) // 或 pages/api/transcript.ts (Pages Router) import { YoutubeTranscript } from "youtube-transcript"; import { NextResponse } from "next/server"; export async function GET(request: Request) { const { searchParams } = new URL(request.url); const videoId = searchParams.get("videoId"); if (!videoId) { return NextResponse.json({ error: "缺少videoId参数" }, { status: 400 }); } try { const transcript = await YoutubeTranscript.fetchTranscript(videoId); return NextResponse.json(transcript); } catch (error) { return NextResponse.json( { error: (error as Error).message }, { status: 500 } ); } }
2. 客户端组件调用API
// app/TranscriptClientComponent.tsx 'use client'; import { useEffect, useState } from "react"; export default function TranscriptClientComponent() { const [transcript, setTranscript] = useState<any[]>([]); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { const getTranscript = async () => { try { const res = await fetch("/api/transcript?videoId=QY8dhl1EQfI"); if (!res.ok) throw new Error("获取字幕失败"); const data = await res.json(); setTranscript(data); } catch (err) { setError((err as Error).message); } finally { setLoading(false); } }; getTranscript(); }, []); if (loading) return <div>加载中...</div>; if (error) return <div>错误:{error}</div>; return ( <div> <h3>视频字幕</h3> <pre>{JSON.stringify(transcript, null, 2)}</pre> </div> ); }
内容的提问来源于stack exchange,提问作者Warren Hogan

