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

基于Next.js与Next-Auth授权实现YouTube视频上传求助

问题:NextAuth Google OAuth授权后实现YouTube上传及频道选择功能

我已通过NextAuth完成Google OAuth授权,目前需要实现两个核心功能:

  1. YouTube视频上传功能
  2. 复刻TunesToTube的频道选择功能——用户登录后可选择自己的YouTube频道

尝试多种方法均未实现频道选择功能,全网搜索也未找到有效解决方案,附上我的route.ts代码:

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

export const authOptions: any = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID ?? "",
      clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
    }),
  ],
  authorization: {
    params: {
      scope: [
        "openid",
        "https://www.googleapis.com/auth/userinfo.email",
        "https://www.googleapis.com/auth/userinfo.profile",
        "https://www.googleapis.com/auth/youtube",
      ].join(" "),
    },
  },
  session: {
    strategy: 'jwt',
  },
  secret: process.env.NEXTAUTH_SECRET,
}

export const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

解决方案

一、实现用户YouTube频道选择功能

1. 调整NextAuth配置,保存并传递access_token

在authOptions中添加回调函数,将Google返回的access_token存入JWT并同步到session,方便后续API调用:

callbacks: {
  async jwt({ token, account }) {
    // 登录时保存access_token和过期时间
    if (account) {
      token.accessToken = account.access_token;
      token.expiresAt = account.expires_at;
      token.refreshToken = account.refresh_token;
    }
    return token;
  },
  async session({ session, token }) {
    // 将token中的凭证传递到前端session
    session.user.accessToken = token.accessToken;
    session.user.expiresAt = token.expiresAt;
    return session;
  },
}

2. 调用YouTube Data API获取用户频道列表

通过access_token调用channels.list接口,获取用户名下所有YouTube频道:

  • 请求地址:GET https://www.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails&mine=true
  • 请求头:Authorization: Bearer {access_token}

返回结果中会包含频道ID、名称、封面等信息,可将这些数据渲染为选择列表供用户选择。

二、实现YouTube视频上传功能

1. 确认权限范围

确保OAuth scope包含https://www.googleapis.com/auth/youtube.upload(已配置的youtube权限已包含该上传权限,无需额外添加)。

2. 后端上传逻辑示例(Next.js API路由)

import { getServerSession } from "next-auth/next";
import { authOptions } from "./auth/[...nextauth]/route";

export async function POST(request: Request) {
  const session = await getServerSession(authOptions);
  if (!session?.user.accessToken) {
    return new Response("Unauthorized", { status: 401 });
  }

  const formData = await request.formData();
  const videoFile = formData.get("video") as File;
  const title = formData.get("title") as string;
  const description = formData.get("description") as string;
  const channelId = formData.get("channelId") as string; // 用户选择的频道ID

  // 构建视频元数据
  const metadata = {
    snippet: {
      title,
      description,
      categoryId: "22", // 视频分类ID,可根据需求调整
      channelId
    },
    status: {
      privacyStatus: "public" // 公开状态:public/private/unlisted
    }
  };

  // 发起multipart上传请求
  const boundary = "youtube-upload-boundary";
  const blobParts = [
    `--${boundary}\r\n`,
    `Content-Type: application/json\r\n\r\n`,
    JSON.stringify(metadata) + "\r\n",
    `--${boundary}\r\n`,
    `Content-Type: ${videoFile.type}\r\n\r\n`,
    videoFile,
    `\r\n--${boundary}--`
  ];

  const uploadResponse = await fetch(
    "https://www.googleapis.com/upload/youtube/v3/videos?part=snippet,status",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${session.user.accessToken}`,
        "Content-Type": `multipart/related; boundary=${boundary}`
      },
      body: new Blob(blobParts)
    }
  );

  const result = await uploadResponse.json();
  return Response.json(result);
}

关键注意事项

  • 需在Google Cloud Console中启用YouTube Data API v3,否则所有API调用会返回权限错误
  • OAuth consent screen需添加所有用到的scope,若面向外部用户需将应用发布为"生产"状态
  • access_token有过期时间,NextAuth会自动用refresh_token刷新(需确保配置中保留了refresh_token的存储)

内容的提问来源于stack exchange,提问作者th1n

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 12:04:51