HomeBlogTranscribePlansAPI Keys

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

C# 控制台应用程序使用 System.Text.Json 反序列化 YouTube 转录文本

dotnet run 到打印转录文本,我的机器上花了大约两秒钟,其中大部分是网络往返。本文中任何地方都没有出现 NuGet 包——在 C# 中获取 YouTube 转录文本是一个 GET 请求,HttpClientSystem.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_ERRORINVALID_URL(400)、VIDEO_NOT_FOUNDTRANSCRIPT_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