Descargar videos de YouTube desde línea de comandos en Ubuntu

He encontrado en YouTube un video con seis horas de música de Vivaldi que está genial para escuchar mientras trabajas. El único problema es que ocupa una pestaña del navegador si lo escuchamos desde YouTube; lo ideal sería tenerlo como MP3 en el disco duro.

La extensión de Google Chrome que uso habitualmente para descargar vídeos (FVD Downloader) no funciona con YouTube. Entonces me acordé de que había visto una entrada en javierin.com que trataba precisamente sobre esto y explicaba cómo hacerlo usando un programa llamado clive, pero a mí no me funcionó.

Rebuscando un poco más encontré uno llamado youtube-dl; este tiene la ventaja de que según Google va poniendo trabas para descargar los vídeos de YouTube ellos van actualizando el programa. Con este fue muy sencillo:

koas@koas-desktop:~$ ./youtube-dl "http://www.youtube.com/watch?v=E2uOGOqIyC4"
[youtube] Setting language
[youtube] Confirming age
[youtube] E2uOGOqIyC4: Downloading webpage
[youtube] E2uOGOqIyC4: Downloading video info webpage
[youtube] E2uOGOqIyC4: Extracting video information
[youtube] E2uOGOqIyC4: Encrypted signatures detected.
[youtube] E2uOGOqIyC4: Downloading js player vflx8EenD
[youtube] E2uOGOqIyC4: Downloading js player vflx8EenD
[download] Destination: Antonio Vivaldi - I Solisti Veneti-E2uOGOqIyC4.mp4
[download] 100% of 641.05MiB in 00:52

Una vez con el fichero .mp4 descargado simplemente hay que extraer el audio. Para ello primero nos aseguramos de tener las librerías y programas necesarios:

sudo apt-get install ffmpeg libavcodec-extra-53 libav-tools

Y hacemos la conversión:

avconv -i "Antonio Vivaldi - I Solisti Veneti-E2uOGOqIyC4.mp4" Vivaldi_6_horas.mp3

La conversión tarda un rato (al fin y al cabo son seis horas de música) pero finalmente obtenemos un MP3 de 528 MB.

Todo este proceso es bastante interesante para aplicaciones web: nos permite descargar vídeos y hacer cosas con ellos como extraer fotogramas para vistas previas y cosas parecidas.

Permitir Cross Origin en Amazon S3

Ya hablé la última vez sobre Amazon S3 y sobre lo útil que es para descargar tráfico de un servidor. Además de imágenes y vídeos también podemos subir a S3 ficheros de fuentes si nuestra página los utiliza.

Sin embargo Firefox no permite cargar fuentes de un dominio que no sea el de la página, así que nos devolverá un error en la consola como este:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ******. This can be fixed by moving the resource to the same domain or enabling CORS

Para solucionar esto debemos entrar en S3, seleccionar nuestro bucket y pulsar en Permissions – Edit CORS configuration. Allí podremos editar la configuración en XML; yo lo tengo así:

<CORSConfiguration>
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
    </CORSRule>
</CORSConfiguration>

En el campo AllowedOrigin tengo un asterisco porque quiero que se puedan leer desde varios dominios distintos, pero si se quisiera restringir solamente a un dominio en lugar del asterisco pondríamos el dominio. Podemos tener tantos nodos CORSRule como necesitemos.

Backup de datos de Amazon S3 a tu disco duro

Amazon S3 funciona estupendamente para descargar de tráfico cualquier página web moviendo los ficheros estáticos (sobre todo imágenes y/o vídeos) a su espacio de almacenamiento. Por precaución siempre es conveniente mantener una copia de seguridad de esos archivos, y buscando información en Google casi todo lo que he encontrado era para hacerlo al revés: hacer copia de seguridad de nuestro disco duro en Amazon S3.

Rebuscando un poco más he encontrado un programa que hace las dos cosas, y bastante más. Se llama DragonDisk y no solamente permite gestionar los buckets de Amazon S3 sino que también permite sincronizar de S3 a nuestro disco duro o viceversa.

Lo he probado con un bucket y funciona perfectamente. Además es multiplataforma (Windows / Mac / GNU Linux).

Máquinas virtuales para Internet Explorer

En cualquier desarrollo web hay que comprobar que se ve correctamente en todos los navegadores. Si usamos GNU Linux no tenemos el Internet Explorer, así que debemos ingeniárnoslas para hacer las pruebas.

La misma gente de Microsoft nos lo pone muy fácil: han creado una página desde donde se pueden descargar ficheros para máquinas virtuales que tienen las versiones de Internet Explorer desde la 6 a la 11. Basta con instalarse  VirtualBox y descargar estas máquinas virtuales para poder hacer pruebas con todas las versiones de Internet Explorer que queramos.

Este es el enlace para la descarga: https://modern.ie/en-us/virtualization-tools#downloads

Posicionar de forma absoluta elementos dentro de celdas en Firefox

Una nota rápida para no tirarme media hora en el futuro sufriendo por esto…

Firefox tiene un bug muy curioso: si intentamos posicionar elementos de forma absoluta dentro de una celda (ya sea una celda de tabla normal o un div con estilo display:table-cell) él amablemente nos enseña el dedo más largo de su peluda patita y los coloca con respecto al primer elemento de fuera de la tabla.

Para evitarlo hay que crear como primer hijo de la celda un div con posición relativa que ocupe todo el ancho y alto de la celda, y meter dentro de él los elementos que queramos posicionar de forma absoluta. Entonces sí funciona.

Redirigir al usuario y continuar trabajando en PHP

Hay veces en las que debemos realizar alguna tarea con PHP que lleva demasiado tiempo (convertir imágenes, videos, etc…). En estos casos no es elegante dejar esperando al usuario, es preferible redirigirle a una página donde se le informe de que se está llevando a cabo la tarea.

Una solución podría ser implementar un sistema de workers que realicen el trabajo; así nuestro script de PHP simplemente tendría que lanzar el trabajo y terminaría inmediatamente. Esta es la mejor solución porque los sistemas de workers permiten un escalado horizontal.

Pero hay ocasiones en las que por las características de nuestro proyecto no se pueden o no se quieren implementar estos workers. En estos casos nos bastará con redirigir al usuario a una página informativa y realizar el trabajo desde nuestro script de PHP.

Esta técnica la encontré, y está perfectamente documentada, en esta página, que podéis consultar para una explicación detallada. Aquí me limito a copiar el código:

    //Redirect to messageToUser.php
    header("Location: messageToUser.php");
    //Erase the output buffer
    ob_end_clean();
    //Tell the browser that the connection's closed
    header("Connection: close");

    //Ignore the user's abort (which we caused with the redirect).
    ignore_user_abort(true);

    //Extend time limit to 30 minutes
    set_time_limit(1800);
    //Extend memory limit to 10MB
    ini_set("memory_limit","10M");
    //Start output buffering again
    ob_start();

    //Tell the browser we're serious... there's really
    //nothing else to receive from this page.
    header("Content-Length: 0");

    //Send the output buffer and turn output buffering off.
    ob_end_flush();
    //Yes... flush again.
    flush();

    //Close the session.
    session_write_close();

    //Do some work

    //Then notify the user that it's finished

Actualización 14/09/2021: si usamos PHP-FPM hay una función que nos permite cerrar la conexión con el navegador y seguir haciendo lo que queramos en nuestro código, fastcgi_finish_request.

Enlaces en páginas web sin especificar protocolo

Últimamente he estado viendo en bastantes sitios que los enlaces a documentos externos se escriben sin especificar el protocolo. Es decir, en lugar de escribir

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

escribiríamos esto

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

Así a bote pronto me ha chocado un poco, porque de toda la vida yo pensaba que era el http:// lo que distinguía a un enlace externo de uno que hacía referencia a un documento dentro del árbol de ficheros de nuestra página.

Pero por lo visto resulta que es el // lo que hace que el navegador interprete el enlace como externo, por lo que nos podemos ahorrar el http o https en la URL siempre que queramos que el enlace use el protocolo http y no otros como ftp, mailto, o tel.

Además tenemos una ventaja añadida si quitamos el protocolo: el navegador usará el protocolo que esté usando la página en la que está el enlace. Si hemos accedido a ella mediante http usará http, y si hemos accedido mediante https usará https. Esto significa que si estamos cargando un fichero con http (contenido no seguro) en una página cargada con https (contenido seguro) el navegador no protestará (que yo recuerde esto solamente lo hace Internet Explorer).

Actualización del 17/11/2015: no es solo Internet Explorer, otros navegadores también bloquean por ejemplo ficheros de JS cargados desde una URL no segura si se ha accedido a la página a través de una URL segura. Un motivo más para usar esta forma de escribir las URL.

Extensión Google Chrome para videos de YouTube que se cuelgan esperando a clickberry.tv

Antes de cambiar de ordenador utilizaba Ubuntu 12.10 32 bits y me pasaba una cosa muy extraña: en muchas páginas había incrustados videos de YouTube que no podía ver; el navegador Google Chrome se quedaba mostrando el mensaje “Esperando a clickberry.tv” durante un buen rato y finalmente mostraba un mensaje de error en el vídeo.

Al cambiar el ordenador e instalar Ubuntu 13.10 64 bits pensaba que el problema se solucionaría pero resulta que no, me sigue pasando lo mismo. Después de mucho buscar en Google no he encontrado ninguna solución, ni siquiera he podido encontrar referencias de que esto le pase a alguien más.

Como el problema era bastante molesto decidí investigar las extensiones de Chrome para intentar solucionar el asunto. Resulta que las extensiones son simplemente HTML, CSS y JS, así que resulta muy sencillo hacer una. Este es el código JS que soluciona el problema:

$(document).find("iframe").each(function()
{
	var src = $(this).attr("src");

	if (src.indexOf("youtube.com/embed") > -1)
	{
		var code = src.split("/embed/")[1].split("?")[0];
		var button = $(document.createElement("div"));
		button.addClass("button");
		button.html(chrome.i18n.getMessage("button_caption"));
		button.click(function()
		{
			window.open("http://www.youtube.com/watch?v=" + code);
		});
		button.insertBefore($(this));
	}
});

Realmente no soluciona el problema, el video incrustado sigue esperando a clickberry.tv, pero la extensión añade un botón encima que abre el video en la página de YouTube, donde se ve sin problemas.

La extensión está disponible de forma gratuita en la Chrome Web Store.

Enviar paquetes UDP desde línea de comandos

Estoy haciendo un programa para poder hacer una consola tipo Firebug pero para código PHP, y la idea es que el PHP envíe datos a esa consola mediante UDP. He empezado a hacer el programa y para hacer pruebas rápidas del servidor necesitaba enviar paquetes UDP. La solución más sencilla es un programa que se llama sendip, que permite enviar paquetes de distintos tipos a cualquier destino.

Para instalarlo en Ubuntu:

sudo apt-get install sendip

Una vez instalado, el uso es muy sencillo:

sudo sendip -p ipv4 -p udp -is 192.168.0.199 -us 5000 -ud 1947 -d "Soy un paquete udp" localhost

Los parámetros son estos:

  • -p ipv4: le indica al programa que usaremos  ipV4.
  • -p udp: le indica al programa que usaremos UDP.
  • -is 192.168.0.199: es la dirección IP de origen del paquete.
  • -us 5000: puerto de origen.
  • -ud 1947: puerto de destino.
  • -d “Soy un paquete udp”: los datos a enviar.
  • localhost: el host al que enviaremos el paquete.

Minimizador de JS y CSS

En las páginas que están en producción es conveniente minimizar el código JavaScript y el CSS para que pesen menos y ayuden a que la velocidad de carga de la página sea rápida. Si bien los navegadores cachean estos ficheros y esto solamente se aplica a la primera carga siempre conviene que sea lo más rápida posible (además de reducir el ancho de banda que consumimos en nuestro servidor).

Hay muchos minimizadores de código JavaScript y de CSS, yo uso JavaScript Minifier simplemente porque tiene un enlace en la misma página a su minimizador de CSS, y hace todo el proceso más rápido.