You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的公开资源,无需用户授权,适合获取公共视频的公开字幕。

  1. 在Google Cloud控制台创建并获取API Key,限制其仅用于YouTube Data API(提升安全性)。
  2. 修改代码替换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。

  1. 添加授权路由引导用户登录授权:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 10:49:59