Este es el primer archivo del sitio que usa Javascript, y un iframe... Lo lamento, pero es necesario para este experimento.

Explicación

Primero, me parece lo más oportuno decir de dónde saqué la inspiración, y fue de este proyecto en Python, que, en realidad, lo único que hace es devolver (me parece buena traducción para return en la jerga programática, y un poquito mejor que "escupir" del Qriollo) una ID de un video de YouTube adquirido desde la API de YouTube (todo legal, ahora nada de scrapping), desde una búsqueda con texto random. El script de 0x01h usaba más o menos así:


(IMG|IMG_|IMG-|DSC-)(número al azar entre el 999 y el 9999)( MOV|.MOV| .MOV)

Bueno, en realidad era exactamente así. Cada uno de los tres factores se elegían con números aleatorios, lo que hacía la elección de los videos aún más aleatoria que simplemente buscar un número, cosa que yo hice.

¿Y por qué decidí usar solo un número en la query en lugar del tan avanzado sistema propuesto por 0x01h? Por una razón muy sencilla: preferencias de resultados. Antes de escribir el sitio con el script de Javascript, jugué un rato con las opciones estándar del script de Python, pero noté que la mayoría de videos que me saltaban eran videos familiares: generalmente de animales, bebés, personas hablándole a otras personas menos a mí, y así. Obviamente, esto ocurre por el texto que se usa para buscar el video; el formato "IMG...número...MOV" es obviamente un default que usan las cámaras para grabar videos, y la gente que sube sus videos a YouTube con ese nombre es porque no les interesan los meta datos del video subido a Internet. He aquí que los resultados que conseguía con las opciones predeterminadas terminan devolviéndome resultados bien normies.

Aparte, como mi plan era hacer una página web que me mostrara el video automáticamente, y como el host de este sitio es Neocities, usar el mismo script que había escrito 0x01h, por más eficiente y maleable que fuera, era muy difícil de implementar sin tener que meterle cantidades enormes de bloat (no tengo conocimiento de, por ejemplo, un framework de Javascript que ejecute un código en Python, y más si éste tiene librerías externas... Aargh.), por lo que me vi obligado a aprender a usar la API de YouTube e implementar su idea al Javascript. Después de todo, Google es un fan tremendo tanto de Javascript como Python, por lo que no me sorprendió que, cuando comencé a leer la documentación de la función de la API, habían ejemplos tanto en Javascript como en Python, PHP, Java, HTML y cURL (esta es la primera vez que uso la API y leo la documentación, esto es nuevo para mí, kek).

Bien, ahora a lo jugoso: expliquemos el código.

Veamos la primera línea del <script> (porque todo lo que se corre está en el mismo archivo .html, no hay ningún secreto, ni siquiera mi clave de API):


randNum = Math.floor((Math.random() * 99999) + 1);

Esto creo que se explica fácil. Veámoslo desde cada paréntesis: en Math.random() * 99999, se agarra un número al azar entre el 0 y el 1, y se multiplica por 99999 (como los números aleatorios tienen decimales infinitos, o bastantes, como se lo quiera pensar, puedo multiplicar el número dado por cualquier número y conseguir un buen número); luego, a ese resultado se le suma 1, porque no me interesa recibir el número 0; luego, el Math.floor(...) le remueve los decimales; y con esto nos quedamos con un número aleatorio prolijo entre el 1 y el 99999, sin decimales.

Con esto ya tenemos lo que vamos a ingresarle a la query. Ahora nos falta hacer la petición.

Tenía dos opciones para usar la API de YouTube: con Javascript o con HTTP plano. Por un momento dudé si era más conveniente usar el método de Javascript, pero luego de ver la barbaridad de librerías y métodos estándar que proporcionaba Google para hacer una petición de API segura, en lugar de un XMLHttpRequest de unas cuantas líneas, opté por el último por una cuestión de sencillez en mi código.

Este es el XMLHttpRequest:


ytGet = new XMLHttpRequest();
ytUrl = 'https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=15&q='
        + randNum + '&safeSearch=none&type=video&videoEmbeddable=true&key=(Clave)'
ytGet.open("GET", ytUrl);
ytGet.send();

Lo que sigue del script es lo que hace cuando consigue los valores, que explicaré más adelante. Primero creamos un nuevo objeto XMLHttpRequest (sí, maldito POO) en nuestra variable ytGet, y luego la URL que le vamos a meter al objeto, llamada ytUrl. Los parámetros que tiene son los siguientes:

¿Vieron que cuando ven parte por parte un pedazo largo de código, es todo mucho más entendible?

El resto de métodos son los necesarios para que el objeto ytGet actúe: ytGet.open("GET", ytUrl); inicia el pedido GET con la URL, y ytGet.send(); lo envía.

En este punto, ya tenemos exactamente lo que necesitamos: 15 videos, sin bloqueo de Contenido Seguro, y que se pueden poner en un embed. Entonces, lo que sigue después de que obtenemos la información va dentro de otro método del XMLHttpRequest, llamado onreadystatechange. El código es este:


ytGet.onreadystatechange=function(){
  if(this.readyState==4 && this.status==200){
    ytJson = JSON.parse(this.responseText);
    ytChoose = ytJson["items"]
               [(Math.floor((Math.random() * ytJson["items"].length) + 1))]
               ["id"]["videoId"];
    document.getElementById("embed").innerHTML = "<center><iframe frameborder=\"0\""
                + "width=\"" + document.getElementById("embed").clientWidth
                + "\" height=\"" + (document.getElementById("embed").clientWidth / 4 * 3)
                + "\" src=\"https://www.youtube.com/embed/" + ytChoose +
                "?autoplay=1\"></iframe></center>";
    }
    else if(this.status==403){
        document.getElementById("embed").innerHTML = "Error 403: Permiso denegado. \
        Probablemente se acabó la cuota de la clave API.";
    }
  }

Parece mucho, pero en realidad son cosas muy sencillas. Desmenucemos esta función.

Primero, puede parecer rara la primera línea, ytGet.onreadystatechange=function(){. Es una forma de asignar una función al método del objeto, en lugar de decirle ytGet.onreadystatechange(nombreDeLaFuncion());, que, a mi parecer, es un exceso de líneas de código, ya que esta función no la vamos a usar en ningún otro lado.

Luego, notamos que todo el pedazo de código importante solo se va a ejecutar si el pedido está en estado 4 ("READY", como dice en la documentación) y nos llegó con código 200 ("OK", en los códigos HTTP), tal como dice en la línea if(this.readyState==4 && this.status==200){.

Ahora nos toca manipular lo que nos llegó de la petición, que es lo importante para que el script funcione. Primero, ytJson = JSON.parse(this.responseText); agarra lo que nos llegó, en forma de texto, y lo convierte en JSON y lo guarda en una nueva variable llamada ytJson. Esto me costó entenderlo al principio, ya que trataba de usar comandos de JSON en un string, en lugar de una variable propiamente en formato JSON.

En la siguiente línea ocurre la segunda parte de la aleatoriedad. La primera parte fue el número aleatorio para buscar los videos. Ahora, en ytChoose = ytJson["items"][(Math.floor((Math.random() * ytJson["items"].length) + 1))]["id"]["videoId"];, se elige la ID del video de YouTube de alguno de los X items que nos llegaron. Pude haber puesto, por ejemplo, otra variable para crear ese número y luego meterlo en este método, pero así me pareció mucho más entretenido de leer, por la razón de que a simple vista parece bastante complejo, pero luego te das cuenta que es solo para economizar variables.

Y para terminar, tenemos el método que hace realidad el proyecto: le pone (más bien reemplaza todo lo que hay dentro con) un iframe en el div con id embed, para organizar un poco las cosas. Para no tener que explicar cada parte del string de la línea, les muestro un resultado de ejemplo, y ahí les digo los parámetros:


<iframe frameborder="0" width="623" height="467.25"
    src="https://www.youtube.com/embed/Dtarc5WlI_s?autoplay=1"></iframe>

El frameborder="0" es para que el cuadro no tenga bordes blancos (lo que caga bastante mi sitio, ya que uso fondo negro). Los parámetros width y height, acá, son números estáticos, pero en realidad el script le asigna el ancho del iframe igual al ancho que tiene el div, es decir, el ancho que tiene en realidad todo el contenido, sin contar los márgenes que les puse con el CSS. Y en cuanto al alto, decidí ponerle 3/4 del ancho, como medida estándar; capaz que el video que salta está en un ratio de 4:3, y esta es la mejor forma de verlo; y si está en otro ratio, como 16:9, tampoco se ve tan mal.

Luego hay otro if (que añadí luego de haber escrito esto), que escribe en el sitio un mensaje de error cuando el pedido, bueno, nos llega con un error. El error 403, en este caso, es de "Acceso denegado", y suele ocurrir porque el sistema bloquea automáticamente mi clave de API (y todas las de mi proyecto, así que no puedo hacer ingeniería colocando 200 claves), una vez que alcancé los puntos máximos; en este caso, los que se gastan más rápido son los puntos diarios. Más abajo explico eso.

Edit: no pienso reescribir los párrafos de arriba. Se me ocurrió algo para implementar: en lugar de que el video de los 15 videos sea elegido al azar, brindaré una lista con cada uno de los videos para que puedan ver (así ahorro un poco de la cuota de la API y vemos más videos aleatorios teniendo menos pedidos, yupi). Ah, y la línea de código que crea el iframe la puse en una función, ahora sí.

Y... eh... bueno, eso es todo. ¿Se quedaron con ganas de más? Yo sí, de escribir y explicar. Pero bueno, una clase breve es, en todo caso, una buena clase.

Igual, el programa no es infalible. Muchas veces el código no arranca porque, por la razón que sea, el número aleatorio que salió no brindó ningún video que encaje con las preferencias, o porque me quedé sin cuota para usar la API (según este artículo, cada búsqueda sale 100 puntos de cuota, y mi clave solo tiene 10.000 puntos diarios máximos, o sea, 100 videos random al día), u otro tipo de problemas con el pedido GET. Igual, nada que no pueda arreglar un rápido F5, o un poco de paciencia.

Pueden hacerle cambios al código—como, primero y principal, no usar mi clave de API para no gastarla y usar una suya propia, o cambiar el formato de la query, o activar el safeSearch (si son bien cobardes). Por ejemplo, se me acaba de ocurrir hacer que el pedido de la API solamente me devuelva listas de reproducción, y ponerme a ver listas de reproducción aleatorias, o que la query use palabras aleatorias sacadas de un diccionario. Las posibilidades son infinitas; yo, igual, me quedé satisfecho con cómo funciona ahora.

Eso fue todo. Como se dan cuenta, mi modo de trabajar es muy raro; me inspiro en un momento para hacer cosas, y luego me tomo un laaaaaaaaaaaaaaaargo rato para que se me ocurra algo que poner acá. Personalmente, a mí siempre me había gustado PetitTube, y me apenó mucho que haya cerrado; la idea de ver videos aleatorios, sin tener realmente el control de lo que se te va a presentar en la pantalla, me había gustado mucho, en especial en cómo funcionaba. Ahora lo entiendo más o menos bien.