HomeBlogTranscribePlansAPI Keys

Golang 中的 YouTube 转录文本:net/http 和三个结构体

Go 程序将 YouTube 转录文本 API 响应解码为结构体

这是整个程序。解释在后面。

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"time"
)

type Transcript struct {
	VideoID     string `json:"videoId"`
	Title       string `json:"title"`
	PubDate     string `json:"pubDate"`
	Content     string `json:"content"`
	ContentSize int    `json:"contentSize"`
	Truncated   bool   `json:"truncated"`
}

type APIError struct {
	Code              string `json:"code"`
	Message           string `json:"message"`
	Status            int    `json:"status"`
	RetryAfterSeconds int    `json:"retryAfterSeconds"`
}

type envelope struct {
	Result *Transcript `json:"result"`
	Error  *APIError   `json:"error"`
}

func main() {
	q := url.Values{}
	q.Set("url", "https://www.youtube.com/watch?v=dQw4w9WgXcQ")

	req, err := http.NewRequest(http.MethodGet,
		"https://youtube2text.org/api/transcribe?"+q.Encode(), nil)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	req.Header.Set("x-api-key", os.Getenv("YT2TEXT_KEY"))

	client := &http.Client{Timeout: 60 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer resp.Body.Close()

	var env envelope
	if err := json.NewDecoder(resp.Body).Decode(&env); err != nil {
		fmt.Fprintln(os.Stderr, "decode:", err)
		os.Exit(1)
	}

	if env.Error != nil {
		fmt.Fprintf(os.Stderr, "%s: %s\n", env.Error.Code, env.Error.Message)
		os.Exit(1)
	}

	fmt.Println(env.Result.Title)
	fmt.Printf("%d chars, truncated=%v\n",
		env.Result.ContentSize, env.Result.Truncated)
}

go run main.go,完成。没有第三方模块,这正是在 Golang 中获取 YouTube 转录文本应该看起来的样子——API 是返回 JSON 的纯 HTTPS,所以 net/httpencoding/json 覆盖所有内容。没有官方的 Go 库,我会论证这是一个特性。

信封技巧

成功响应将数据放在 result 下;失败将结构化对象放在 error 下。解码两者到一个具有两个指针字段的结构体中意味着单个 Decode 调用处理每个响应——哪个指针非零就是你的答案。不需要重新读取主体、不需要嗅探 Content-Type,如果你想双重检查,env.Error.Status 匹配 HTTP 状态。

值得 switch 语句的代码

APIError.Code 是稳定的,所以在它上面分支而不是在消息字符串上:

switch env.Error.Code {
case "RATE_LIMIT_EXCEEDED":
	wait := time.Duration(env.Error.RetryAfterSeconds) * time.Second
	time.Sleep(wait) // then retry the request
case "TRANSCRIPT_UNAVAILABLE":
	// video exists but has no caption track — skip permanently
case "VIDEO_NOT_FOUND", "INVALID_URL":
	// bad input; log and move on
default:
	// UNAUTHORIZED, VALIDATION_ERROR, YOUTUBE_ERROR
}

TRANSCRIPT_UNAVAILABLE 是人们处理不当的那个。转录文本来自 YouTube 的字幕轨道——手动或自动生成、优先英文、否则第一个可用语言。如果视频关闭了字幕,就没有转录文本要获取,用指数退避重试不会让字幕增长。在批处理作业中将其视为永久跳过,否则你的工人队列填满了僵尸。

参数和密钥

总共两个查询参数。url 接受任何 YouTube URL 形式或裸露的 11 字符 id。maxChars 限制输出长度(150000 是默认和最大值)——当转录文本馈送到 LLM 时有用,你按令牌付费,这是我听说的大多数 Go 用例,通常对整个频道的 RAG

对于无需任何注册的密钥,curl https://youtube2text.org/api/demo-key 返回一个工作的——在所有匿名用户中每个 IP 每月 5 个视频共享,所以它严格用于尝试事物。如果你在权衡是否调用 yt-dlp 获取字幕文件,我写了yt-dlp 字幕对 API 的比较;如果你的栈的一部分是 Node,JavaScript 中的同一调用更短。

对于真实用途,在 youtube2text.org/app/keys 用 Google 登录——无卡、无电话、每月 5 个视频的免费层、从 50 个 5.99 美元起到无限 19.99 美元的付费层。上面的 Go 代码不改变;只有环境变量改变。