YouTube Subtitles
Extensión de Chrome para descargar en .srt los subtítulos de un vídeo de YouTube, nativos, automáticos o traducidos, desde un botón en la propia página. No está publicada, porque obtener los subtítulos pasa por manejar el reproductor de YouTube de una forma que choca con sus términos de uso. Es un proyecto de aprendizaje y de uso personal.

- commits
- 72
- 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)
- 144
- 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
.srtcon 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.
- 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.
- También desde el popup, con tema claro u oscuro y en español e inglés.
- Sin servidor. Todo pasa en la pestaña de YouTube y en tu navegador.
Por qué no está publicada
YouTube solo entrega los subtítulos a peticiones que llevan un token de atestación (el PoToken) que genera el propio reproductor. La extensión no lo falsifica: hace que el reproductor pida la pista y recoge la respuesta. Aun así, automatizar el reproductor para sacar contenido entra en terreno que los términos de YouTube no permiten, y depende de APIs internas sin documentar que ya se rompieron una vez sin avisar. La uso yo y 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.
Problemas que resolví
- Respuestas
200 OKvacías. El endpoint de subtítulos dejó de servir contenido a labaseUrldel vídeo. La causa era el PoToken, y la solución, dejar que lo genere el reproductor. - Los datos del primer vídeo.
ytInitialPlayerResponseno cambia al navegar dentro de YouTube. Ahora se intercepta/youtubei/v1/playery 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 comoapplication/x-subripy con un enlace<a download>.