Transcripción de YouTube en Ruby: Net::HTTP y Cero Gemas

El reflejo de Ruby es bundle add something antes de escribir una línea de código. Resiste durante cinco minutos: obtener una transcripción de YouTube en Ruby no necesita nada fuera de la biblioteca estándar, porque la API es un GET HTTPS devolviendo JSON, y net/http más json han estado en cada instalación de Ruby desde siempre. Al final de este post tendrás un script que toma cualquier URL de YouTube e imprime el título del video y la transcripción, con una declaración case cubriendo cada error que la API devuelve — y un Gemfile vacío.
Sí, la interfaz de Net::HTTP es tan incómoda como su reputación. Aún son solo veinte líneas.
El script
require "net/http"
require "json"
require "uri"
uri = URI("https://youtube2text.org/api/transcribe")
uri.query = URI.encode_www_form(
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
)
req = Net::HTTP::Get.new(uri)
req["x-api-key"] = ENV.fetch("YT2TEXT_KEY")
res = Net::HTTP.start(uri.hostname, uri.port,
use_ssl: true, read_timeout: 60) do |http|
http.request(req)
end
data = JSON.parse(res.body)
if res.is_a?(Net::HTTPSuccess)
transcript = data["result"]
puts transcript["title"]
puts "#{transcript['contentSize']} chars, truncated: #{transcript['truncated']}"
puts transcript["content"][0, 400]
else
err = data["error"]
case err["code"]
when "RATE_LIMIT_EXCEEDED"
puts "Rate limited. Retry in #{err['retryAfterSeconds']}s."
when "TRANSCRIPT_UNAVAILABLE"
puts "No captions on this video. Skip it."
when "VIDEO_NOT_FOUND", "INVALID_URL"
puts "Bad video reference: #{err['message']}"
else
abort "#{err['code']}: #{err['message']}"
end
end
El parámetro url es indulgente — URLs completas de watch, enlaces cortos de youtu.be, shorts, o un id de video de 11 caracteres desnudo funcionan todos. Agregue maxChars: 20000 a encode_www_form si la transcripción se va a un prompt de LLM y preferiría no pagar por los tokens de un podcast de tres horas; 150000 es el predeterminado y el máximo.
Si tu base de código ya tiene Faraday o httparty, bien, úsalos — la solicitud es los mismos tres ingredientes: el punto final, una cadena de consulta, y un encabezado. A propósito, req["Authorization"] = "Bearer #{key}" funciona idénticamente a x-api-key si los tokens de portador se ajustan mejor a tu configuración.
Una clave en un comando
curl https://youtube2text.org/api/demo-key
Eso devuelve {"success":true,"apiKey":"yt_..."} sin cuenta, sin correo electrónico, nada. La captura: es una clave compartida para todos los usuarios anónimos, limitada a 5 videos por mes por IP. Excelente para verificar que el script funciona, mal para nada que desplegarías.
Lo que la transcripción realmente es
Vale la pena ser claro aquí: el texto viene de las pistas de subtítulos de YouTube. Los subtítulos cargados manualmente se leen hermosamente; los generados automáticamente ocasionalmente destruyen la jerga y los nombres de productos, y no hay nada que ninguna API pueda hacer al respecto ascendente. Los pistas de inglés se prefieren, con alternativa al primer idioma disponible. Y un video con subtítulos desactivados devuelve TRANSCRIPT_UNAVAILABLE — la rama case anterior la trata como un salto, no un reintento, porque reintentar un video que no tiene subtítulos es una forma muy paciente de no lograr nada.
A dónde van los Rubyistas con esto
El patrón que veo más es procesamiento por lotes: recorrer los ids de video de un canal, obtener cada transcripción, alimentarlos a algo que va más abajo. Los estudiantes convierten videos de conferencias en notas de estudio; si lo que va más abajo es Claude, el paseo transcripciones gratuitas en Claude conecta los dos sin pegamento personalizado. El mismo punto final de PHP es casi línea por línea idéntico — comparación aquí si tu equipo ejecuta ambos.
Cuando la clave demo compartida se agota, inicia sesión con Google en youtube2text.org/app/keys — sin teléfono, sin tarjeta. Obtienes tu propia clave gratuita a 5 videos al mes, con niveles pagados desde $5.99 por 50 hasta $19.99 ilimitado. El script no cambia; ENV["YT2TEXT_KEY"] sí.