使用 HttpClient 和 Records 的 C# YouTube 转录文本

从 dotnet run 到打印转录文本,我的机器上花了大约两秒钟,其中大部分是网络往返。本文中任何地方都没有出现 NuGet 包——在 C# 中获取 YouTube 转录文本是一个 GET 请求,HttpClient 加 System.Text.Json 多年来已经包含在框中。到最后,你会有一个完整的控制台应用,具有响应的记录类型、异步 Main,和读取 API 的结构化错误对象而不是从状态码猜测的错误处理。
三个记录
API 成功时返回 {"result": {...}},失败时返回 {"error": {...}},所以直接对信封建模:
public record Transcript(
string VideoId,
string Title,
string PubDate,
string Content,
int ContentSize,
bool Truncated);
public record ApiError(
string Code,
string Message,
int Status,
int? RetryAfterSeconds);
public record Envelope(Transcript? Result, ApiError? Error);
一个 Deserialize<Envelope> 调用处理每个响应。哪个属性非空就是你的答案。RetryAfterSeconds 是可空的,因为只有速率限制错误携带它。
程序
针对 .NET 8,在 6 和 7 上按原样工作:
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
public static class Program
{
private static readonly HttpClient Http = new();
private static readonly JsonSerializerOptions Json =
new(JsonSerializerDefaults.Web);
public static async Task Main(string[] args)
{
var videoUrl = args.Length > 0
? args[0]
: "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
var request = new HttpRequestMessage(HttpMethod.Get,
"https://youtube2text.org/api/transcribe" +
$"?url={Uri.EscapeDataString(videoUrl)}&maxChars=40000");
request.Headers.Add("x-api-key",
Environment.GetEnvironmentVariable("YT2TEXT_KEY"));
var response = await Http.SendAsync(request);
var envelope = JsonSerializer.Deserialize<Envelope>(
await response.Content.ReadAsStringAsync(), Json);
if (envelope?.Error is { } err)
{
Console.Error.WriteLine($"{err.Code}: {err.Message}");
if (err.RetryAfterSeconds is int wait)
Console.Error.WriteLine($"Retry after {wait}s");
Environment.Exit(1);
}
var t = envelope!.Result!;
Console.WriteLine($"{t.Title} ({t.ContentSize} chars, truncated: {t.Truncated})");
Console.WriteLine(t.Content[..Math.Min(t.Content.Length, 400)]);
}
}
JsonSerializerDefaults.Web 将 API 的 camelCase 字段映射到 PascalCase 记录属性,所以没有单个 [JsonPropertyName] 属性。url 参数接受所有 YouTube URL 变体加裸露的 11 字符 id,如果更适合你的中间件,标头可以是 Authorization: Bearer yt_... 而不是 x-api-key。
关于那个 maxChars=40000
maxChars 限制转录文本长度;150000 既是默认值也是硬上限。如果视频的字幕超过你的上限,Truncated 返回 true,你得到前 N 个字符——所以在假设你有完整文本之前检查标志。我在示例中设置 40000,因为这些转录文本通常最终进入 LLM 提示,按大约每令牌 4 字符的规则,那是一个 10k 令牌预算。如果你的模型调用通过 OpenAI Responses API,有一种方法完全跳过 HTTP 管道通过MCP 服务器与 OpenAI;Anthropic 等价物在使用转录文本与 Claude API。
分支处理的错误代码
ApiError.Code 是一个稳定的字符串:UNAUTHORIZED(401)、VALIDATION_ERROR 和 INVALID_URL(400)、VIDEO_NOT_FOUND 和 TRANSCRIPT_UNAVAILABLE(404)、RATE_LIMIT_EXCEEDED(429)、YOUTUBE_ERROR(500)。令人惊讶的是 TRANSCRIPT_UNAVAILABLE:转录文本来自 YouTube 的字幕轨道,一个其所有者禁用字幕的视频没有什么可返回的——没有重试政策能修复那个。在代码上进行模式匹配,将其视为永久跳过。
对于零注册测试密钥,GET https://youtube2text.org/api/demo-key——它是共享的,每个 IP 每月限制 5 个视频,所以它仅用于实验。如果 Go 也在你的栈中,Golang 中的同一调用使用相同的信封技巧。
完整的机器可读参考——每个参数、每个错误,准备粘贴到系统提示中——在 youtube2text.org/api.md。