Tools 2026年3月6日

AI 视频矩阵:YouTube OAuth 授权完整配置指南

从零搭建 YouTube 自动上传流水线:Google Cloud 项目、OAuth 授权、多频道 Token 管理,附完整 Python 脚本。

W

Woody

woodyrush.com

AI 视频矩阵:YouTube OAuth 授权完整配置指南

如果你在搭建视频自动化流水线——AI 生成脚本、TTS 配音、程序化合成视频、定时发布——你需要 YouTube Data API v3 来实现无人工干预的自动上传。

YouTube 上传 API 要求每个频道都通过 OAuth 2.0 授权,产生一个 refresh token。你的服务器存储这个 token,之后就能代表该频道 7×24 小时自动上传视频,不需要再打开浏览器。

这篇指南覆盖完整流程:创建 Google Cloud 项目、配置 OAuth、对多个频道授权、验证上传是否成功——所有容易踩的坑都会提前说明。

完成后你会拥有:

  • 1 个 Google Cloud 项目(所有频道共用)
  • 1 个 OAuth 客户端(Desktop 类型)
  • 每个 YouTube 频道 1 个 refresh token,存入数据库
  • 可从任意 Python 脚本或任务队列调用的上传流程

第一步:创建 Google Cloud 项目

  1. 进入 console.cloud.google.com
  2. 点击顶部项目下拉框 → New Project
  3. 项目名随意,如 video-pipelineyt-uploader
  4. 创建 → 等约 30 秒 → 从下拉框选中新项目

一个项目管理所有频道,不需要每个账号单独建项目。

第二步:启用 YouTube Data API v3

  1. 进入 APIs & Services → Library
  2. 搜索 YouTube Data API v3
  3. 点击 → Enable
  4. 可选:同时启用 YouTube Analytics API(后续拉取数据指标用)

第三步:配置 OAuth 授权页面

这一步是最多人卡住的地方,严格按步骤来:

  1. 进入 APIs & Services → OAuth consent screen
  2. 选择 External(除非你有 Google Workspace 组织账号,那可以选 Internal)
  3. 填写:
    • App name:随便填,如 Video Uploader(用户不会看到)
    • User support email:你的邮箱
    • Developer contact email:你的邮箱
  4. Save and Continue

添加 Scope 权限

  1. 点击 Add or Remove Scopes
  2. 添加以下三个 scope:
    • https://www.googleapis.com/auth/youtube.upload — 上传视频
    • https://www.googleapis.com/auth/youtube — 管理频道(元数据、播放列表)
    • https://www.googleapis.com/auth/youtube.readonly — 读取频道信息
  3. Save and Continue

添加测试用户

  1. 关键步骤:应用处于”Testing”状态时,只有明确列出的测试用户才能完成 OAuth 授权。你必须把矩阵里每个频道对应的 Google 账号邮箱都加进来。
  2. 点击 Add Users → 逐一输入 Google 账号邮箱
  3. Save and Continue → 返回 Dashboard

Testing 模式 vs Production 模式

应用默认处于 Testing 模式,有两点必须知道:

  • Testing 模式下 refresh token 7 天后过期。也就是说你的自动化每周都会断掉,需要重新授权。
  • 要彻底解决,需要提交 Production 验证。Google 审核需要数天到数周。通过后 refresh token 永不过期。
  • MVP 阶段可以先不管,token 过期了重新跑授权脚本,一个账号 30 秒搞定。

第四步:创建 OAuth 客户端凭证

  1. 进入 APIs & Services → Credentials
  2. 点击 Create Credentials → OAuth client ID
  3. Application type 选 Desktop app ← 重要,不要选 “Web application”
  4. 名称随意,如 video-uploader
  5. 点击 Create
  6. 下载 JSON 文件 → 保存为项目目录下的 client_secret.json

这个 JSON 文件所有频道共用。频道之间的区分在第五步授权时自动区分,不在这里。

为什么选 Desktop app 而不是 Web? Desktop 流程使用本地重定向(localhost),不需要你搭一个 Web 服务器来接收 OAuth 回调。本地跑脚本,浏览器里点授权,拿到 token,结束。

第五步:对每个频道授权

每个 YouTube 账号各跑一次以下脚本。脚本会打开浏览器,用拥有该频道的 Google 账号登录,授权后返回 refresh token 供存储。

安装依赖

pip install google-api-python-client google-auth-oauthlib

授权脚本

"""
youtube_oauth.py — 对一个 YouTube 频道进行 API 上传授权。
每个频道运行一次,用拥有该频道的 Google 账号登录。
输出一个包含 refresh token 和频道信息的 JSON 文件。
"""
import json
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

SCOPES = [
    "https://www.googleapis.com/auth/youtube.upload",
    "https://www.googleapis.com/auth/youtube",
    "https://www.googleapis.com/auth/youtube.readonly",
]

def authorize():
    flow = InstalledAppFlow.from_client_secrets_file(
        "client_secret.json", SCOPES
    )
    # 打开浏览器窗口 — 登录并授权
    credentials = flow.run_local_server(port=8090)

    # 验证:拉取频道信息,确认授权成功
    youtube = build("youtube", "v3", credentials=credentials)
    response = youtube.channels().list(part="snippet", mine=True).execute()

    if not response.get("items"):
        print("错误:该 Google 账号没有 YouTube 频道。")
        print("先在 https://www.youtube.com/create_channel 创建频道")
        return

    channel = response["items"][0]
    channel_id = channel["id"]
    channel_title = channel["snippet"]["title"]
    print(f"\n✅ 授权成功:{channel_title} ({channel_id})")

    token_data = {
        "channel_id": channel_id,
        "channel_title": channel_title,
        "access_token": credentials.token,
        "refresh_token": credentials.refresh_token,
        "token_uri": credentials.token_uri,
        "client_id": credentials.client_id,
        "client_secret": credentials.client_secret,
        "scopes": list(credentials.scopes),
    }

    filename = f"tokens_{channel_id}.json"
    with open(filename, "w") as f:
        json.dump(token_data, f, indent=2)

    print(f"💾 Token 已保存到 {filename}")
    print(f"🔑 Refresh token:{credentials.refresh_token[:20]}...")
    print(f"\n将此 refresh token 存入数据库,用于后续自动上传。")

if __name__ == "__main__":
    authorize()

每个频道重复一次: 跑脚本 → 用账号 #1 登录 → 保存 token。再跑 → 用账号 #2 登录 → 保存 token。每次运行生成一个独立的 tokens_{channel_id}.json

第六步:验证上传是否正常

接入流水线之前,先确认每个频道都能实际上传。

生成测试视频

# 5 秒黑屏无声 — 仅用于测试上传流程
ffmpeg -f lavfi -i color=c=black:s=1080x1920:d=5 \
       -f lavfi -i anullsrc \
       -shortest -c:v libx264 -c:a aac \
       test_video.mp4

测试上传脚本

"""
test_upload.py — 上传测试视频,验证 OAuth 是否正常。
用法:python test_upload.py tokens_UCxxxx.json test_video.mp4
以 PRIVATE 上传,不会公开出现。测试完在 YouTube Studio 删除即可。
"""
import sys
import json
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload

def test_upload(token_file: str, video_file: str):
    with open(token_file) as f:
        tokens = json.load(f)

    credentials = Credentials(
        token=tokens["access_token"],
        refresh_token=tokens["refresh_token"],
        token_uri=tokens["token_uri"],
        client_id=tokens["client_id"],
        client_secret=tokens["client_secret"],
        scopes=tokens["scopes"],
    )

    youtube = build("youtube", "v3", credentials=credentials)

    body = {
        "snippet": {
            "title": "Test Upload — Delete Me",
            "description": "自动化流水线测试,可以删除。",
            "tags": ["test"],
            "categoryId": "28",  # 科学与技术
        },
        "status": {
            "privacyStatus": "private",  # 不公开
            "selfDeclaredMadeForKids": False,
        },
    }

    media = MediaFileUpload(video_file, chunksize=-1, resumable=True)
    request = youtube.videos().insert(
        part="snippet,status",
        body=body,
        media_body=media,
    )

    response = None
    while response is None:
        status, response = request.next_chunk()
        if status:
            print(f"上传中... {int(status.progress() * 100)}%")

    video_id = response["id"]
    print(f"\n✅ 上传成功!")
    print(f"🎬 视频 ID:{video_id}")
    print(f"🔗 URL:https://youtube.com/watch?v={video_id}")
    print(f"\n(已以 PRIVATE 上传,测试完请在 YouTube Studio 删除)")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("用法:python test_upload.py <token_file.json> <video_file.mp4>")
        sys.exit(1)
    test_upload(sys.argv[1], sys.argv[2])

对每个频道各跑一次:

python test_upload.py tokens_UCxxxx.json test_video.mp4

看到”上传成功”,该频道就可以接入自动化流水线了。


在流水线中使用 Token

Token 存入数据库后,上传代码通过以下方式重建凭证并自动刷新:

from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build

def get_youtube_client(oauth_tokens: dict):
    """从存储的 token 构建已认证的 YouTube 客户端。"""
    credentials = Credentials(
        token=None,  # 会自动用 refresh_token 刷新
        refresh_token=oauth_tokens["refresh_token"],
        token_uri=oauth_tokens.get("token_uri", "https://oauth2.googleapis.com/token"),
        client_id=oauth_tokens["client_id"],
        client_secret=oauth_tokens["client_secret"],
    )
    return build("youtube", "v3", credentials=credentials)

google-auth 库会自动处理 access token 刷新。只要 refresh token 有效,你就再也不需要打开浏览器。


配额与限制

指标数值
每项目每日配额10,000 单位
每次视频上传消耗约 1,600 单位
每次元数据更新消耗约 50 单位
安全的每日上传量约 6 个视频
5 个账号 × 1 视频/天约 8,000 单位 ✅
10 个账号 × 2 视频/天约 32,000 单位 ❌ 需申请扩容

申请扩容:Google Cloud Console → APIs & Services → YouTube Data API v3 → Quotas → Edit Quotas → 填写理由。通常 1–3 个工作日审批通过。

所有频道的上传共享同一个项目配额,规模化时需提前规划。


常见报错排查

“Access blocked: This app’s request is invalid” → 第四步选了 “Web application” 而不是 “Desktop app”。删掉重建。

“The caller does not have permission” → 该 Google 账号没有加入第三步的测试用户列表。添加后重试。

“Token has been expired or revoked” → 应用还在 Testing 模式,7 天过去了。重新跑 youtube_oauth.py 授权。永久解决方案:提交 Production 验证。

“quotaExceeded” → 已达到每日 10,000 单位限额。太平洋时间午夜重置。申请配额扩容。

上传成功,但 YouTube Studio 显示”Processing failed” → 视频文件损坏或编码不支持。YouTube 要求 H.264 视频 + AAC 音频的 MP4 容器。用 ffprobe your_video.mp4 确认。

“The request metadata specifies an invalid video description” → 描述超过 5,000 字,或包含 YouTube 屏蔽的 URL。建议控制在 4,000 字以内。

“Daily Limit for Unauthenticated Use Exceeded” → 凭证没有正确传递。检查存储的 token 中 refresh_token 是否为 null。


安全注意事项

  • 不要把 client_secret.json 或 token 文件提交到 git。 两者都加入 .gitignore
  • Refresh token 等同于密码。 有条件的话存入数据库时加密。
  • 一个 OAuth 客户端管理所有频道完全没问题。 频道身份区分来自 refresh token,不是客户端。
  • 撤销授权: 如果某个账号被盗,在 myaccount.google.com/permissions 撤销授权,对应 refresh token 立即失效。
#youtube #oauth #python #自动化 #ai视频