Actualizando la extensión de Chrome tras el cierre de Google Feed API

Google Feed API

Jose María Acuña, administrador del blog JMaCuna73, me escribía a principios de semana para avisarme que nuestra extensión para Chrome había dejado de funcionar debido a que Google abandonaba el soporte de Google Feed API (EN), el servicio que utilizaba para cargar los últimos artículos en ella.

La compañía tiene un historial curioso de servicios que en su momento se posicionaron como referentes y, debido al peso que tienen en el mercado, acaban por estrangular a la competencia aún cuando dejan de mantenerlos. Ocurrió en su momento con Google Reader, y está ocurriendo a día de hoy con Feedburner.

Pero como todo en esta vida, basta que uno caiga para que surjan alternativas más actualizadas a los tiempos que corren. Eso mismo ha pasado con Google Feed API, y Jose María estuvo a bien enlazarme un artículo en el que explica varios métodos para mostrar en un HTML el contenido de un RSS (ES).

Estos días, y aprovechando algunos huecos libres en la agenda, estuve modificando el código de la extensión y ya puedo anunciar que la versión 2.2 de la extensión de Actualidad Tecnológica vuelve a estar operativa.

Puede descargarla de forma totalmente gratuita en el market de Chrome (ES), y he aprovechado para optimizar el código (la extensión pesa ahora 1kb menos), eliminar el formulario de suscripción por un simple enlace y arreglar algunos bugs menores que tenía (como algunos fallos que daba el buscador).

A mi entender resulta una manera muy cómoda de ver si hay novedades en el sector o realizar búsquedas sobre temas que ya hemos tratado con anterioridad. No envía notificaciones ni nada por el estilo. Simplemente se queda ahí hasta que el usuario la pincha, muestra el contenido, y vuelve a ocultarse hasta nueva orden.

Le animo a que la pruebe y me de su feedback. También tenemos aplicación para Chrome (ES), que es simplemente un enlace directo que se crea en el apartado Aplicaciones del navegador, pero sin lugar a dudas la extensión es muchísimo más útil.

Y por si se lo pregunta, ya he hablado del porqué aún no tenemos versión para Firefox. Al menos, hasta que en efecto el nuevo estándar funcione como cabría esperar.

Hasta aquí la noticia. Ahora voy a explicar para aquellos que estén interesados la parte técnica.

¿Cómo lo he hecho?

En mi caso me he decidido por utilizar la API de RSS to JSON online converter (EN). El servicio hace lo que todos esperaríamos (convertir un RSS a JSON), y Jose María pegaba un ejemplo que es el que ha usado para actualizar su propia extensión.

En mi caso, sin embargo, no resultaba tan sencillo habida cuenta de que todos mis artículos empiezan con una imagen en tamaño 1200×568, que por supuesto se aleja de las dimensiones óptimas para una extensión de navegador. Y además, el número de caracteres que ocupa el código de la imagen es irregular, ya que WordPress a veces le mete mierda y además depende del atributo ALT que yo le coloque. Esto hacía que el entry[].content.substring(0, 600) que utiliza Jose María a veces me mostrara la imagen y un poco de texto extra, a veces no me la mostrara. Como en mis artículos el primer párrafo tampoco tiene gran importancia (ya sabe que en estos dominios paso bastante de las reglas del SEO), tuve que buscar alternativas para preprocesar entry[].content (la variable JSON que tiene el contenido de cada artículo) sacando de allí únicamente la ruta de la imagen para luego hacer con ella lo que me viniera en gana.

Y por si se lo pregunta, entry[].img no me devuelve la imagen destacada del artículo, sino que aplica una imagen específica semejante para cada uno (que por cierto, ni siquiera existe en mi servidor). Creo que el problema puede venir en cómo Feedburner gestiona las imágenes destacadas, pero por ahora es un tema que tengo pendiente mirar (si sabe algo al respecto, estaré gustoso de oírle :)).

Después de varios intentos di con la solución aplicando reglas:

 var regex = /<img.*?src=['"](.*?)['"]/;
 var src = regex.exec(entry[i].content.substring(0, 700))[1];

En la primera línea creo una regla que busca el contenido que haya justo después del primer “<img src=”, y en la segunda lo asocio a la variable src, analizando los primeros 700 caracteres (más de 700 caracteres no creo que llegue a ocupar el código inicial en ningún momento) de entry[].content y quitándole de paso las comillas o comillas dobles (así me cubro las espaldas a futuros cambios) a la URL.

Ya con ella, ha sido cosa de limpiar el código y adaptarlo al nuevo formato. Tampoco estaba interesado en mostrar la fecha de publicación (publico a diario, así que poca importancia debería tener esto para el usuario), pero que sepa que puede incluirla invocándola con un date.toDateString().substr(4). Superpuse varios divs para que cada elemento esté formado por la imagen destacada, por un div superior que oscurece la imagen (un div con opacidad 0.5 y background negro) y el título de la misma en grande por encima, que al estar lo de abajo oscurecido puede leerse indistintamente de la imagen de fondo que haya utilizado. Todo enlazado al artículo, claro está.

El código final, por si a alguien le sirve, sería el siguiente:

 <div id="feed"></div>
 <script> 
    (function(){
       var url = "http://feeds.feedburner.com/ElBlogDePabloyglesias"; //el rss del contenido que queramos mostrar
       var xhr = createCORSRequest("GET", "https://api.rss2json.com/v1/api.json?rss_url=" + url);
       if (!xhr) {
          throw new Error('CORS not supported');
       } else {
          xhr.send();
       }
       xhr.onreadystatechange = function () {
          if (xhr.readyState == 4 && xhr.status == 200) { 
             var responseText = xhr.responseText;
             var result = JSON.parse(responseText);
             var container = document.getElementById("feed"), entry = result.items, date;
             var regex = /<img.*?src=['"](.*?)['"]/; //la regla que buscará la imagen en el JSON
             for (var i = 0; i < 5; i++) { //cambiar el 5 por el número de elementos que queremos mostrar, o dejarlo en entry.lenght para que tome el número de elementos totales del feed
                dv = document.createElement("div");
                date = new Date(entry[i].pubDate);
                var src = regex.exec(entry[i].content.substring(0, 700))[1]; //la URL de la imagen
                dv.innerHTML = '<div class="entry"><a href="' + entry[i].link + '" target="_blank"><div class="article"><img src="' + src + '"></div><div class="black"></div><div class="title"><h3>' + entry[i].title + '</h3></div></a><div>';
                container.appendChild(dv); //mostramos en el div feed el contenido de cada artículo formateado al diseño que aplicaremos en el CSS del final
             }
          }
       }
    })();
    function createCORSRequest(method, url) {
       var xhr = new XMLHttpRequest();
       if ("withCredentials" in xhr) {
          xhr.open(method, url, true);
       } else if (typeof XDomainRequest != "undefined") {
          xhr = new XDomainRequest();
          xhr.open(method, url);
       } else {
          xhr = null;
       }
       return xhr;
    }
 </script>
 <style type="text/css">
    img{max-width:100%;max-height:187px !important}
   .opacity{margin-top:10px;margin-bottom:10px;}
   .title{margin-top:-100px;padding:10px;z-index:2;position:relative;}
   .black{opacity: 0.5;background-color:black;height:187px;margin-top:-191px;z-index:1;}
 </style>

No es que sea mi mejor trabajo (estoy seguro que se puede solucionar de una manera mucho más elegante la estructuración del feed), pero al menos cumple con los objetivos y carga relativamente rápido (prácticamente un segundo, segundo y medio de latencia).

Y sobra decir que en este caso ha sido aplicado en el desarrollo de una extensión para navegador, pero que en la práctica podemos utilizarlo para mostrar en cualquier lugar un número de elementos sacados de un RSS, sea en un artículo de un blog, en una página o plugin, en una aplicación web…

Cualquier espacio donde nos permitan incluir código HTML5 y hacer llamadas externas puede ser utilizado para estos menesteres.

Muchas gracias a Jose María por avisar, y espero que le sirva :).