YouTube Transkription i Golang: net/http och tre structs

Här är hela programmet. Förklaring efter.
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, klart. Inga tredjepartsmoduler, vilket är hur man får en YouTube-transkription i Golang bör se ut — API:t är ren HTTPS som returnerar JSON, så net/http och encoding/json täcker allt. Det finns inget officiellt Go-bibliotek för detta, och jag skulle hävda att det är en egenskap.
Kuverttricket
Lyckade svar lägger data under result; misslyckanden lägger ett strukturerat objekt under error. Avkodning av båda i en struct med två pekarfält betyder att ett enda Decode-anrop hanterar varje svar — vilken pekare som helst som är icke-nil är ditt svar. Ingen omläsning av kroppen, ingen sniffning av Content-Type, och env.Error.Status matchar HTTP-statusen om du vill dubbelkontrollera.
Koder värda ett switch-uttalande
APIError.Code är stabil, så grena på den i stället för på meddelandesträngar:
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 är den folk misshandlar. Transkriptioner kommer från Youtubes bildtextsspår — manuella eller automatiskt genererade, engelska föredraget, första tillgängliga språk annars. Om en video har bildtexter inaktiverade finns det ingen transkription att hämta, och omprövning med exponentiell backoff kommer inte att göra bildtexterna växa. Behandla det som ett permanent hopp i batch-jobb, eller din arbetarkö fylls med zombies.
Parametrar och nycklar
Två frågeparametrar totalt. url tar valfri YouTube URL-form eller ett enkelt 11-tecken-id. maxChars begränsar utmatningslängd (150000 är standard och max) — användbart när transkriptionen matar en LLM och du betalar per token, vilket är mest Go-användningsfall jag hör om, vanligtvis RAG över en hel kanal.
För en nyckel utan någon registrering returnerar curl https://youtube2text.org/api/demo-key en fungerade — delad mellan alla anonyma användare på 5 videor per månad per IP, så det är strikt för att prova saker. Om du väger detta mot att skala ut till yt-dlp för undertextfiler, skrev jag en jämförelse av yt-dlp-undertexter vs API:t; och om del av din stack är Node är samma anrop i JavaScript ännu kortare.
För verklig användning, logga in med Google på youtube2text.org/app/keys — inget kort, ingen telefon, gratis nivå på 5 videor per månad, betalade nivåer från $5,99 för 50 upp till $19,99 unlimited. Go-koden ovan förändras inte; bara env-variabeln gör.