Node.js调用YouTube Data API获取字幕时权限错误求助
解决YouTube Data API调用时的"No access, refresh token..."错误
问题描述
在Node.js服务器中使用Google API提取YouTube视频字幕,已在Google Cloud启用YouTube Data API并配置了正确的Client ID和Client Secret,但调用captions.list方法时抛出错误:
Error: No access, refresh token, API key or refresh handler callback is set.
完整代码如下:
const express = require("express"); const { google } = require("googleapis"); const { OAuth2 } = google.auth; const router = express.Router(); const clientId =process.env.CLIENT_ID; const clientSecret = process.env.CLIENT_SECRET; const redirectUri = process.env.REDIRECT-URI; const oauth2Client = new OAuth2(clientId, clientSecret, redirectUri); // Set up YouTube API client const youtube = google.youtube({ version: "v3", auth: oauth2Client, }); router.get("/:videoId", async (req, res) => { try { const { videoId } = req.params; // Call the captions.list method to get the captions for the specified video const captionsResponse = await youtube.captions.list({ part: "snippet", videoId: videoId, }); if (captionsResponse.data.items.length === 0) { res.status(400).json({ success: false, message: "This video doesn't have a transcript!", }); return; } // Get the ID of the first caption const captionId = captionsResponse.data.items[0].id; // Call the captions.download method to get the transcript text const transcriptResponse = await youtube.captions.download({ id: captionId, tfmt: "vtt", // Specify the format of the transcript (e.g., WebVTT) }); const transcriptText = transcriptResponse.data; res.status(200).json({ success: true, transcript: transcriptText }); } catch (error) { console.error(error); res .status(500) .json({ success: false, message: "Failed to retrieve transcript" }); } }); module.exports = router;
错误原因
当前代码仅初始化了OAuth2客户端,但未完成授权流程获取有效的access token或refresh token;另外,若仅需访问公共视频的公开字幕,OAuth2并非必需,使用API Key即可完成请求。
解决方案
根据你的使用场景选择以下两种方案之一:
方案1:使用API Key访问公共视频的公开字幕(推荐,流程更简单)
API Key可直接访问YouTube的公开资源,无需用户授权,适合获取公共视频的公开字幕。
- 在Google Cloud控制台创建并获取API Key,限制其仅用于YouTube Data API(提升安全性)。
- 修改代码替换OAuth2配置为API Key:
const express = require("express"); const { google } = require("googleapis"); const router = express.Router(); // 替换为你的API Key,从环境变量读取 const apiKey = process.env.YOUTUBE_API_KEY; // 初始化YouTube客户端,使用API Key鉴权 const youtube = google.youtube({ version: "v3", auth: apiKey, }); router.get("/:videoId", async (req, res) => { try { const { videoId } = req.params; const captionsResponse = await youtube.captions.list({ part: "snippet", videoId: videoId, }); if (captionsResponse.data.items.length === 0) { res.status(400).json({ success: false, message: "This video doesn't have a transcript!", }); return; } const captionId = captionsResponse.data.items[0].id; const transcriptResponse = await youtube.captions.download({ id: captionId, tfmt: "vtt", }); const transcriptText = transcriptResponse.data; res.status(200).json({ success: true, transcript: transcriptText }); } catch (error) { console.error(error); res.status(500).json({ success: false, message: "Failed to retrieve transcript" }); } }); module.exports = router;
注意:API Key仅能访问公开资源,无法获取私有视频字幕或进行修改操作。
方案2:完成OAuth2授权流程(用于访问私有视频或需要用户授权的场景)
若需访问私有视频字幕,必须完成OAuth2授权流程获取用户的access token和refresh token。
- 添加授权路由引导用户登录授权:
const express = require("express"); const { google } = require("googleapis"); const { OAuth2 } = google.auth; const router = express.Router(); const clientId = process.env.CLIENT_ID; const clientSecret = process.env.CLIENT_SECRET; const redirectUri = process.env.REDIRECT_URI; // 注意原代码中变量名是REDIRECT-URI,这会报错,需改为REDIRECT_URI const oauth2Client = new OAuth2(clientId, clientSecret, redirectUri); const youtube = google.youtube({ version: "v3", auth: oauth2Client, }); // 生成授权URL的路由 router.get("/auth", (req, res) => { // 所需权限范围,根据需求选择 const scopes = [ "https://www.googleapis.com/auth/youtube.force-ssl", "https://www.googleapis.com/auth/youtubepartner" ]; const authUrl = oauth2Client.generateAuthUrl({ access_type: "offline", // 获取refresh token,用于token过期时自动刷新 scope: scopes, }); res.redirect(authUrl); }); // 授权回调路由,交换授权码获取token router.get("/auth/callback", async (req, res) => { const { code } = req.query; const { tokens } = await oauth2Client.getToken(code); oauth2Client.setCredentials(tokens); // 建议将tokens存储到数据库,后续请求可直接读取使用 res.send("授权成功,现在可以访问字幕接口"); }); // 原字幕获取路由 router.get("/:videoId", async (req, res) => { try { const { videoId } = req.params; const captionsResponse = await youtube.captions.list({ part: "snippet", videoId: videoId, }); if (captionsResponse.data.items.length === 0) { res.status(400).json({ success: false, message: "This video doesn't have a transcript!", }); return; } const captionId = captionsResponse.data.items[0].id; const transcriptResponse = await youtube.captions.download({ id: captionId, tfmt: "vtt", }); const transcriptText = transcriptResponse.data; res.status(200).json({ success: true, transcript: transcriptText }); } catch (error) { console.error(error); res.status(500).json({ success: false, message: "Failed to retrieve transcript" }); } }); module.exports = router;
注意:
- 原代码中
redirectUri = process.env.REDIRECT-URI会导致语法错误,需改为REDIRECT_URI(环境变量名不能包含减号)。 - 授权完成后,需确保
oauth2Client已设置有效的tokens才能调用字幕接口。
内容的提问来源于stack exchange,提问作者Omar
相关产品推荐
相关产品推荐

