Transkrypcja YouTube w C# z HttpClient i Records

Od dotnet run do wydrukowanej transkrypcji zajęło mi około dwóch sekund na mojej maszynie, a większość z tego to podróż sieciowa. Żadne pakiety NuGet nie pojawiają się gdziekolwiek w tym poście — uzyskanie transkrypcji YouTube w C# to jedno żądanie GET, a HttpClient plus System.Text.Json były wysyłane w pudełku przez lata. Na koniec będziesz mieć kompletną aplikację konsolową z typami rekordów dla odpowiedzi, asynchroniczny Main i obsługę błędów czytającą ustrukturyzowany obiekt błędu API zamiast zgadywania na podstawie kodów stanu.
Trzy rekordy
API zwraca {"result": {...}} na sukces i {"error": {...}} na niepowodzenie, więc modeluj kopertę bezpośrednio:
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);
Jedno wywołanie Deserialize<Envelope> obsługuje każdą odpowiedź. Która właściwość jest nie-null jest Twoją odpowiedzią. RetryAfterSeconds jest nullable, ponieważ tylko błędy limitów szybkości go niosą.
Program
Celuje na .NET 8, działa tak jak jest na 6 i 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 mapuje pola API w camelCase na właściwości rekordu PascalCase, więc nie ma nawet pojedynczego atrybutu [JsonPropertyName] w zasięgu. Parametr url akceptuje każdą odmianę URL YouTube plus gołe 11-znakowe id, a nagłówek może być Authorization: Bearer yt_... zamiast x-api-key, jeśli to lepiej pasuje do Twojego middleware.
O tym maxChars=40000
maxChars ogranicza długość transkrypcji; 150000 to zarówno domyślna, jak i twarda górna granica. Jeśli napisy wideo przebiegają Twoją granicę, Truncated wraca true i otrzymujesz pierwsze N znaków — sprawdź flagę przed założeniem, że masz pełny tekst. Ustawiłem 40000 w przykładzie, ponieważ te transkrypcje zwykle kończą się w promptu LLM, a przy około czterech znakach na token to budżet 10k-tokenów. Jeśli Twoje wywołania modelu przechodzą przez API OpenAI Responses, istnieje sposób, aby pominąć całą głębiowanie HTTP całkowicie poprzez serwer MCP z OpenAI; odpowiednik Anthropic jest w używaniu transkrypcji z Claude API.
Kody błędów do rozgałęzienia
ApiError.Code to stabilny ciąg: UNAUTHORIZED (401), VALIDATION_ERROR i INVALID_URL (400), VIDEO_NOT_FOUND i TRANSCRIPT_UNAVAILABLE (404), RATE_LIMIT_EXCEEDED (429), YOUTUBE_ERROR (500). Ten, który zaskakuje ludzi, to TRANSCRIPT_UNAVAILABLE: transkrypcje pochodzą z pasów napisów YouTube, a film, którego właściciel wyłączył napisy, ma nic do zwrócenia — brak polityki ponownych prób tego naprawia. Dopasuj wzorzec do kodu, traktuj to jako permanentne pominięcie.
Aby uzyskać klucz testowy bez rejestracji, POBIERZ https://youtube2text.org/api/demo-key — jest wspólny i ograniczony do 5 filmów na miesiąc na IP, więc dotyczy eksperymentów tylko. Jeśli Go jest również w Twoim stosie, to samo wywołanie w Golang używa sztuczki koperty identycznej.
Kompletne odniesienie czytelne dla maszyn — każdy parametr, każdy błąd, gotowy do wklejenia do promptu systemu — znajduje się na youtube2text.org/api.md.