← Proyectos
Extensión

SubFetch for YouTube

Extensión de Chrome que descarga en .srt los subtítulos de un vídeo de YouTube, sean nativos, automáticos o traducidos, y que también proyecta sobre el reproductor tus propios .srt o .vtt con ajustes de accesibilidad. Antes se llamaba YouTube Subtitles. Aún no está publicada en la Chrome Web Store; puede que lo esté más adelante.

El botón Subtítulos en la barra de acciones de YouTube, junto a Me gusta y Compartir, con el menú abierto mostrando una pista en inglés generada automáticamente y los botones Traducir y .SRT
commits
90
commits
mundos de ejecución (ISOLATED y MAIN)
2
mundos de ejecución (ISOLATED y MAIN)
capas para detectar la navegación SPA
5
capas para detectar la navegación SPA
tests (Vitest)
208
tests (Vitest)

Qué hace

  • Un botón más en la barra del vídeo. Junto a Me gusta y Compartir aparece un botón Subtítulos con el mismo aspecto que los de YouTube. Al pulsarlo, un menú lista las pistas del vídeo.
  • Nativos y automáticos. Cada pista dice si la subió el autor o la generó YouTube, y se descarga en .srt con el título del vídeo, el idioma y el tipo en el nombre.
  • Traducción automática. Las pistas traducibles se pueden descargar ya traducidas a cualquier idioma del catálogo de YouTube.
  • Tus propios subtítulos sobre el vídeo. Cargas un .srt o un .vtt, desde el menú del botón o desde el popup, y se ven encima del reproductor, también a pantalla completa. Si van desfasados, se ajustan en saltos de medio segundo.
  • Ajustes de accesibilidad. Tamaño, tipografía (incluida OpenDyslexic), color del texto y del fondo, contorno de alto contraste y posición vertical, que también se cambia arrastrando el subtítulo. Hay cuatro preajustes y una vista previa en directo, y la configuración se guarda entre sesiones.
  • Sin tocar lo que estás viendo. Si tenías los subtítulos apagados o en otro idioma, después de la descarga el reproductor vuelve a quedarse como estaba.
  • En ocho idiomas, con tema claro u oscuro: español, inglés, francés, alemán, portugués, italiano, japonés y chino.
  • Sin servidor. Todo pasa en la pestaña de YouTube y en tu navegador.

Estado

Todavía no está en la Chrome Web Store. Puede que la publique más adelante, y el cambio de nombre va en esa dirección: la Store no admite extensiones cuyo nombre empiece por "YouTube", y la fórmula aceptada es la de SubFetch for YouTube™. Lo que más pesa en contra es el mantenimiento, porque la descarga depende de APIs internas del reproductor sin documentar que ya se rompieron una vez sin avisar. Por ahora la uso yo. No tiene repositorio público.

Cómo está hecho

Tres contextos y dos mundos

El botón y el popup son Vue 3. El content script vive en el mundo aislado de la extensión, y un segundo script se inyecta en el mundo de la página (MAIN) para poder hablar con #movie_player y envolver fetch y XMLHttpRequest. Los dos se comunican con window.postMessage, mensajes tipados y un requestId por petición.

El reproductor pide, la extensión escucha

Para descargar una pista, el script de MAIN la activa con setOption('captions', 'track', …), espera la petición a /api/timedtext que hace el reproductor (con su token), se queda con la respuesta y deja los subtítulos como estaban.

Una SPA que no avisa

YouTube cambia de vídeo sin recargar la página. Detectarlo bien obliga a combinar sus eventos internos, popstate, un parche de history.pushState en MAIN, un MutationObserver y un sondeo de respaldo, además de cachear la respuesta del reproductor por vídeo.

Una capa dentro del reproductor

Los subtítulos propios se pintan en un segundo Shadow DOM montado dentro de #movie_player, que es el elemento que entra en pantalla completa. Un parser propio convierte el .srt o el .vtt en cues en milisegundos, y el overlay actualiza el tiempo en cada fotograma del vídeo con requestVideoFrameCallback. Los ajustes viven en el almacenamiento de la extensión, y lo que cambias en el popup llega al instante al vídeo.

Problemas que resolví

  • Respuestas 200 OK vacías. El endpoint de subtítulos dejó de servir contenido a la baseUrl del vídeo. La causa era el PoToken, y la solución, dejar que lo genere el reproductor.
  • Los datos del primer vídeo. ytInitialPlayerResponse no cambia al navegar dentro de YouTube. Ahora se intercepta /youtubei/v1/player y la caché se vacía al empezar cada navegación.
  • Un botón que desaparecía. YouTube recicla la barra de acciones sin cambiar de vídeo. El montaje comprueba en qué contenedor vive el botón, no solo si existe.
  • Ficheros .txt. Chrome cambiaba la extensión de las descargas. Ahora se descargan como application/x-subrip y con un enlace <a download>.
  • Subtítulos que se saltaban líneas. Los .srt que salen de los subtítulos automáticos de YouTube tienen cues que se solapan, y la búsqueda binaria solo encontraba uno. Ahora se muestran todos los que están activos a la vez.
  • Subtítulos a saltos. El evento timeupdate del vídeo llega pocas veces por segundo y el texto cambiaba con retraso. El tiempo se lee ahora en cada fotograma.

Stack

WXTManifest V3Vue 3TypeScriptTailwind CSS 4vue-i18nShadow DOMVitest

Artículos