Cuando tengo que descargar algún vídeo suelo utilizar yt-dlp, una herramienta de línea de comandos diseñada para descargar videos y audios desde una amplia variedad de sitios web.
yt-dlp
es un fork de la popular herramienta youtube-dl
, pero incluye muchas características adicionales y mejoras. Por ejemplo, es compatible con protocolos de transmisión adaptativa como DASH y HLS, lo que permite descargar segmentos y reconstruirlos en un archivo completo.
Ahora bien, cuando la página web no es una de las soportadas por el proyecto o no se consigue detectar el stream de vídeo correctamente, lo más sencillo es intentar buscarlo mediante las Developer Tools del navegador.
DASH y HLS
DASH (Dynamic Adaptive Streaming over HTTP) es un protocolo de transmisión que divide un video o audio en múltiples segmentos pequeños que se descargan y reproducen de manera dinámica.
Permite ajustar la calidad de reproducción en tiempo real dependiendo del ancho de banda disponible, garantizando una experiencia sin interrupciones. Se basa en ficheros de control (como un manifiesto o playlist.json
en este caso) que indican cómo acceder a los segmentos de video y audio.
HLS (HTTP Live Streaming) es otro protocolo de transmisión adaptativa, desarrollado por Apple.
Similar a DASH, divide el contenido en pequeños fragmentos y usa listas de reproducción (.m3u8
) para coordinar la reproducción. Se utiliza ampliamente en dispositivos y servicios compatibles con Apple, pero también tiene soporte en otras plataformas.
Ambos protocolos son fundamentales para la transmisión en streaming porque permiten entregar contenido de alta calidad de manera eficiente, independientemente de las condiciones de la red del usuario.
playlist.json
En el caso que nos ocupa, después de abrir las herramientas de desarrollo usando F12
, se pueden filtrar las peticiones XHR realizadas por el navegador cuando se reproduce el vídeo para encontrar el fichero playlist.json
:
XHR (XMLHttpRequest) es un mecanismo en los navegadores que permite realizar solicitudes HTTP (GET, POST, etc.) desde JavaScript. Se usa para obtener datos (como el fichero JSON en nuestro caso) desde un servidor sin necesidad de recargar la página.
Estructura del fichero JSON
La estructura de este fichero JSON, simplificada bastante para dejar únicamente un bloque de vídeo y uno de audio, es la siguiente:
{
"clip_id": "<guid_del_clip>",
"base_url": "../url/base/del/video/",
"video": [
{
"id": "<guid_del_video>",
"format": "dash",
"mime_type": "video/mp4",
"codecs": "avc1.64001F",
"bitrate": 1254000,
"avg_bitrate": 808000,
"duration": 159.92,
"framerate": 25,
"width": 960,
"height": 540,
"init_segment": "<segmento_init_en_Base64>",
"index_segment": "<guid_del_segmento_index>.mp4?<parámetros>&range=806-1161",
"segments": [
{
"url": "<guid_del_segmento_1>.mp4?<parámetros>&range=1162-312730",
"size": 311568
},
{
"url": "<guid_del_segmento_n>.mp4?<parámetros>&range=15637804-15996276",
"size": 358472
}
]
}
],
"audio": [
{
"id": "<guid_del_audio>",
"format": "dash",
"mime_type": "audio/mp4",
"codecs": "mp4a.40.2",
"bitrate": 195000,
"avg_bitrate": 194000,
"duration": 159.936,
"channels": 2,
"sample_rate": 48000,
"init_segment": "<segmento_init_en_Base64>",
"init_segment_url": "",
"index_segment": "<guid_del_segmento_index>.mp4?<parámetros>&range=678-1033",
"segments": [
{
"url": "<guid_del_segmento_1>.mp4?<parámetros>&range=1034-146462",
"size": 145428
},
{
"url": "<guid_del_segmento_n>.mp4?<parámetros>&range=3780771-3880609",
"size": 99838
}
],
"audio_primary": true
}
]
}
Propiedades del fichero JSON
Las propiedades principales de este fichero son video
y audio
, las cuales contienen las listas de configuraciones de video y audio disponibles para ser reproducidas.
En cada bloque de video o audio (puede haber más de uno de cada) se especifican las características del mismo:
format
: El formato del bloque (por ejemplo,dash
)mime_type
: El tipo MIME, que describe el tipo de archivo (por ejemplo,video/mp4
oaudio/mp4
)codecs
: El codec utilizado para codificar el bloque (por ejemplo,avc1
omp4a
)bitrate
: La tasa de bits del bloque, que afecta la calidadsegments
: Una lista de fragmentos que forman ese bloque
Cada segmento de vidoe o audio tiene, como mínimo, los siguientes valores:
url
: La dirección específica para descargar ese fragmentosize
: El tamaño del fragmento, útil para validaciones
init_segment
Además, en el caso de este JSON concreto, se definen un init_segment
y un index_segment
que juegan un papel importante dentro de DASH y otros protocolos de transmisión adaptativa.
El init_segment
(segmento de inicialización) contiene los metadatos necesarios para comenzar la reproducción de las pistas de video o audio. Estos metadatos incluyen información como el formato del archivo, las cabeceras del contenedor (por ejemplo, MP4), los detalles del codec, y otra información esencial para decodificar y reproducir los segmentos posteriores.
Este segmento no contiene datos de video o audio directamente reproducibles, pero es obligatorio para que el reproductor pueda interpretar los bloques posteriores.
En algunos ficheros JSON (como el que analizamos), el init_segment
puede estar directamente embebido en el JSON como una cadena codificada en Base64. Esto permite que el cliente (por ejemplo, un reproductor o un script) lo decodifique y utilice sin necesidad de descargar un archivo separado.
La codificación Base64 transforma los datos binarios en texto que es seguro para transmitir en JSON, ya que no incluye caracteres que puedan romper el formato o ser malinterpretados, por ejemplo:
"init_segment": "AAAAHGZ0eXBkYXNoAAAAAGRhc2htcDQyaXNvNg..."
En este caso, sería necesario decodificar el contenido para generar un archivo binario válido. Solo se descarga una vez por pista (video o audio) y se utiliza como base para interpretar todos los fragmentos (o segments
) de esa pista.
index_segment
El index_segment
(segmento de índice) proporciona información adicional sobre cómo están estructurados y ordenados los segments
. Puede contener datos sobre las ubicaciones byte a byte de los segmentos dentro de la pista o servir como referencia para acceder a los fragmentos específicos.
En muchos casos, el index_segment
no es necesario descargarlo explícitamente, ya que los reproductores pueden inferir su contenido o acceder directamente a las URLs de los segments
.
Aunque en algunos sistemas de transmisión adaptativa el index_segment
es un archivo real, en otros, se trata de información implícita que el reproductor utiliza al leer el manifiesto (como en el JSON que estás trabajando).
Script en PowerShell
Una vez conocemos los detalles del protocolo DASH y el contenido del fichero playlist.json
, se puede construir un script en PowerShell que lo procese de la siguiente manera:
- Obtención del JSON
- Descargar el fichero JSON inicial que contiene información de los bloques de video y audio
- Selección dinámica
- Permitir al usuario elegir qué configuración de video y audio desea descargar (ordenadas y presentadas de manera clara)
- Descarga y concatenación de segmentos
- Los segmentos de video y audio seleccionados se descargan pieza por pieza usando
curl.exe
- Se valida cada descarga para garantizar que los archivos sean correctos
- Los segmentos descargados se combinan de manera incremental (usando
cmd.exe /c copy /b
) para formar un único archivo de video o audio
- Los segmentos de video y audio seleccionados se descargan pieza por pieza usando
- Combinación de video y audio
- Al final, se utiliza FFmpeg para fusionar el archivo de video y audio en un único archivo reproducible
No proporciono el código fuente del script, más allá de las capturas anteriores, porque cada web requiere una programación diferente. Ahora tienes las bases para hacerlo tú mismo y Microsoft Copilot es tu amigo ;-)
Ejemplo de ejecución
Una vez se ha creado y probado el script en PowerShell, llega la hora de ejecutarlo:
PS C:\Users\Test> .\Download-DASH-Video-v10.ps1
cmdlet Download-DASH-Video-v10.ps1 at command pipeline position 1
Supply values for the following parameters:
Url: https://web-video.com/videos/34f1eb03-7655-4ab8-cd25-a4abad796c92/playlist.json
Title: Test Video
Bloques de Video disponibles:
Número Formato MIME_Type Codecs Bitrate Avg_Bitrate Framerate Ancho Alto
------ ------- --------- ------ ------- ----------- --------- ----- ----
5 dash video/mp4 avc1.64002A 5418000 3970000 25 1920 1080
1 dash video/mp4 avc1.640020 2673000 1753000 25 1280 720
2 dash video/mp4 avc1.64001F 1475000 1062000 25 960 540
3 dash video/mp4 avc1.64001E 745000 554000 25 640 360
4 dash video/mp4 avc1.640015 328000 276000 25 426 240
Introduce el número del bloque de video que deseas descargar: 5
Bloques de Audio disponibles:
Número Formato MIME_Type Codecs Bitrate Avg_Bitrate Canales Frecuencia
------ ------- --------- ------ ------- ----------- ------- ----------
1 dash audio/mp4 mp4a.40.2 196000 194000 2 48000
3 dash audio/mp4 opus 70000 68000 2 48000
2 dash audio/mp4 opus 102000 99000 2 48000
Introduce el número del bloque de audio que deseas descargar: 1
init_segment de video descargado y configurado como base: C:\Users\Test\Video\video_final.mp4
Segmento 1 descargado correctamente: C:\Users\Test\Video\segmento_video_1.mp4 (Tamaño: 1835244)
Segmento de video concatenado: C:\Users\Test\Video\video_final.mp4
[...]
Segmento 134 descargado correctamente: C:\Users\Test\Video\segmento_video_134.mp4 (Tamaño: 2009319)
Segmento de video concatenado: C:\Users\Test\Video\video_final.mp4
init_segment de audio descargado y configurado como base: C:\Users\Test\Audio\audio_final.mp4
Segmento 1 descargado correctamente: C:\Users\Test\Audio\segmento_audio_1.mp4 (Tamaño: 145680)
Segmento de audio concatenado: C:\Users\Test\Audio\audio_final.mp4
[...]
Segmento 134 descargado correctamente: C:\Users\Test\Audio\segmento_audio_134.mp4 (Tamaño: 137496)
Segmento de audio concatenado: C:\Users\Test\Audio\audio_final.mp4
Video y audio combinados exitosamente en: C:\Users\Test\Test Video.mp4
Historial de cambios
- 2025-03-22: Documento inicial