20 de mayo de 2015

Canal de videos de YouTube con API v3 embebido en una página con Javascript.

Aunque mi fuerte no es el desarrollo de software, me gusta ensayar y desarrollar soluciones que sirvan a las empresas. Hace poco fue necesario desarrollar un canal para mostrar los videos subidos a un canal por una empresa. La solución con la API v2 se encuentra en varias páginas de internet y ha sido implementada por varias páginas con bastante éxito; sin embargo con el cambio de la API v2 a la API v3 aparecieron inconvenientes y las páginas que dependían de esta implementación empezaron a recibir la notificación por parte de YouTube desde el 22 de abril y a principios de mayo estos desarrollos dejaron de funcionar definitivamente.

Aunque en la página de la API v3 de Youtube aparecen varios ejemplos, todos dependen de la autenticación con OAuth. Para desarrollarla con la API KEY no aparecen ejemplos y me tomó bastante tiempo desarrollarla.

Pues bien, sin más preámbulos explicaré la forma para desarrollarlo haciendo modificaciones en la conocida implementación en la API v2.

El código original desarrollado por Simone Gianni en Fiddle puede encontrarse en http://jsfiddle.net/SimoneGianni/NmvA9/ y es el siguiente:



/*
Copyright 2011 : Simone Gianni <simoneg@apache.org>

Released under The Apache License 2.0
http://www.apache.org/licenses/LICENSE-2.0

*/

(function() {
    function createPlayer(jqe, video, options) {
        var ifr = $('iframe', jqe);
        if (ifr.length === 0) {
            ifr = $('<iframe scrolling="no">');
            ifr.addClass('player');
        }
        var src = 'http://www.youtube.com/embed/' + video.id;
        if (options.playopts) {
            src += '?';
            for (var k in options.playopts) {
                src+= k + '=' + options.playopts[k] + '&';
            }
            src += '_a=b';
        }
        ifr.attr('src', src);
        jqe.append(ifr);
    }
  
    function createCarousel(jqe, videos, options) {
        var car = $('div.carousel', jqe);
        if (car.length === 0) {
            car = $('<div>');
            car.addClass('carousel');
            jqe.append(car);
           
        }
        $.each(videos, function(i,video) {
            options.thumbnail(car, video, options);
        });
    }
   
    function createThumbnail(jqe, video, options) {
        var imgurl = video.thumbnails[0].url;
        var img = $('img[src="' + imgurl + '"]');
        if (img.length !== 0) return;
        img = $('<img>');   
        img.addClass('thumbnail');
        jqe.append(img);
        img.attr('src', imgurl);
        img.attr('title', video.title);
        img.click(function() {
            options.player(options.maindiv, video, $.extend(true,{},options,{playopts:{autoplay:1}}));
        });
    }
   
    var defoptions = {
        autoplay: false,
        user: null,
        carousel: createCarousel,
        player: createPlayer,
        thumbnail: createThumbnail,
        loaded: function() {},
        playopts: {
            autoplay: 0,
            egm: 1,
            autohide: 1,
            fs: 1,
            showinfo: 0
        }
    };
   
   
    $.fn.extend({
        youTubeChannel: function(options) {
            var md = $(this);
            md.addClass('youtube');
            md.addClass('youtube-channel');
            var allopts = $.extend(true, {}, defoptions, options);
            allopts.maindiv = md;
            $.getJSON('http://gdata.youtube.com/feeds/users/' + allopts.user + '/uploads?alt=json-in-script&format=5&callback=?', null, function(data) {
                var feed = data.feed;
                var videos = [];
                $.each(feed.entry, function(i, entry) {
                    var video = {
                        title: entry.title.$t,
                        id: entry.id.$t.match('[^/]*$'),
                        thumbnails: entry.media$group.media$thumbnail
                    };
                    videos.push(video);
                });
                allopts.allvideos = videos;
                allopts.carousel(md, videos, allopts);
                allopts.player(md, videos[0], allopts);
                allopts.loaded(videos, allopts);
            });
        }
    });
   
})();
       
$(function() {
    $('#player').youTubeChannel({user:'youtube'});
}); 


y contiene además un código en HTML para cargar el canal.

Con un poco de búsqueda encontré la página http://stackoverflow.com/questions/22613903/youtube-api-v3-get-list-of-users-videos de la cual tomé el siguiente código para recuperar la lista de videos:


// Get Uploads Playlist
$.get(
   "https://www.googleapis.com/youtube/v3/channels",{
   part : 'contentDetails',
   forUsername : 'USER_CHANNEL_NAME',
   key: 'YOUR_API_KEY'},
   function(data) {
      $.each( data.items, function( i, item ) {
          pid = item.contentDetails.relatedPlaylists.uploads;
          getVids(pid);
      });
  }
);

//Get Videos
function getVids(pid){
    $.get(
        "https://www.googleapis.com/youtube/v3/playlistItems",{
        part : 'snippet',
        maxResults : 20,
        playlistId : pid,
        key: 'YOUR_API_KEY'},
        function(data) {
            var results;
            $.each( data.items, function( i, item ) {
                results = '<li>'+ item.snippet.title +'</li>';
                $('#results').append(results);
            });
        }
    );
}


<!--In your HTML -->
<ul id="results"></ul>



Y finalmente combinando ambos códigos se encuentra lo siguiente:

Lo primero que se debe cambiar en el segundo código es que no queremos obtener solamente el título sino además el thumbnail del video y el id del video para poderlo reproducir; así que se cambia la línea:

results = '<li>'+ item.snippet.title +'</li>';

por

            var video = {
                        title: item.snippet.title,
                        id: item.snippet.resourceId.videoId,
                        thumbnails: item.snippet.thumbnails.default
                    };
            videos.push(video);

y se elimina la línea

$('#results').append(results);

Ahora, claramente con el cambio de la API no nos servirá la instrucción:

$.getJSON('http://gdata.youtube.com/feeds/api/videos?author=' + allopts.user + '&orderby=published&alt=json-in-script&format=5&callback=?', null, function(data)

y debe ser reemplazada por los códigos que propone la página 2:

// Get Uploads Playlist
$.get(
   "https://www.googleapis.com/youtube/v3/channels",{
   part : 'contentDetails',
   forUsername : 'USER_CHANNEL_NAME',
   key: 'YOUR_API_KEY'},
   function(data) {
      $.each( data.items, function( i, item ) {
          pid = item.contentDetails.relatedPlaylists.uploads;
          getVids(pid);
      });
  }
);

//Get Videos
function getVids(pid){
    $.get(
        "https://www.googleapis.com/youtube/v3/playlistItems",{
        part : 'snippet',
        maxResults : 20,
        playlistId : pid,
        key: 'YOUR_API_KEY'},
        function(data) {
            var results;
            $.each( data.items, function( i, item ) {
                var video = {
                        title: item.snippet.title,
                        id: item.snippet.resourceId.videoId,
                        thumbnails: item.snippet.thumbnails.default
                    };
                    videos.push(video);
            });
        }
    );


Es muy importante tener en cuenta el cambio en la estructura de datos que retorna la API de YouTube; estos cambios implican cambiar estos variables:

title: entry.title.$t debe cambiarse por title: item.snippet.title,

id: entry.id.$t.match('[^/]*$') debe cambiarse por id: item.snippet.resourceId.videoId,

thumbnails: entry.media$group.media$thumbnail debe cambiarse por
thumbnails: item.snippet.thumbnails.default

Una vez realizados estos cambios el código definitivo queda:


/*
Copyright 2011 : Simone Gianni <simoneg@apache.org>

Released under The Apache License 2.0
http://www.apache.org/licenses/LICENSE-2.0

*/

(function() {
    function createPlayer(jqe, video, options) {
        var ifr = $('iframe', jqe);
        if (ifr.length === 0) {
            ifr = $('<iframe scrolling="no">');
            ifr.addClass('player');
        }
        var src = 'http://www.youtube.com/embed/' + video.id;
        if (options.playopts) {
            src += '?';
            for (var k in options.playopts) {
                src+= k + '=' + options.playopts[k] + '&';
            } 
            src += '_a=b';
        }
        ifr.attr('src', src);
        jqe.append(ifr); 
    }
   
    function createCarousel(jqe, videos, options) {
        var car = $('div.carousel', jqe);
        if (car.length === 0) {
            car = $('<div>');
            car.addClass('carousel');
            jqe.append(car);
           
        }
        $.each(videos, function(i,video) {
            options.thumbnail(car, video, options);
        });
    }
   
    function createThumbnail(jqe, video, options) {
        var imgurl = video.thumbnails.url;
        var img = $('img[src="' + imgurl + '"]');
        if (img.length !== 0) return;
        img = $('<img>');   
        img.addClass('thumbnail');
        jqe.append(img);
        img.attr('src', imgurl);
        img.attr('title', video.title);
        img.click(function() {
            options.player(options.maindiv, video, $.extend(true,{},options,{playopts:{autoplay:1}}));
        });
    }
   
    var defoptions = {
        autoplay: false,
        user: null,
        carousel: createCarousel,
        player: createPlayer,
        thumbnail: createThumbnail,
        loaded: function() {},
        playopts: {
            autoplay: 0,
            egm: 1,
            autohide: 1,
            fs: 1,
            showinfo: 0
        }
    };
   
   
    $.fn.extend({
        youTubeChannel: function(options) {
            var md = $(this);
            md.addClass('youtube');
            md.addClass('youtube-channel');
            var allopts = $.extend(true, {}, defoptions, options);
            allopts.maindiv = md;
            // Obtener las playlist
            $.get("https://www.googleapis.com/youtube/v3/channels",{
                part : 'contentDetails',
                forUsername : 'USUARIO_DEL_CANAL',
                key: 'SU_API_KEY'},
                function(data) {
                    $.each( data.items, function( i, item ) {
                        pid = item.contentDetails.relatedPlaylists.uploads;
                        getVids(pid);
                    });
                }
            );
           
            //Obtener la información de cada video
            function getVids(pid){
                $.get("https://www.googleapis.com/youtube/v3/playlistItems",{
                    part : 'snippet',
                    maxResults : 20,
                    playlistId : pid,
                    key: 'SU_API_KEY'},
                    function(data) {
                        var feed = data.feed;
                        var videos = [];
                        $.each( data.items, function( i, item ) {
                            var video = {
                                title: item.snippet.title,
                                id: item.snippet.resourceId.videoId,
                                thumbnails: item.snippet.thumbnails.default
                            };
                        videos.push(video);
                        });
                        allopts.allvideos = videos;
                        allopts.carousel(md, videos, allopts);
                        allopts.player(md, videos[6], allopts);
                        allopts.loaded(videos, allopts);
                    }
                );
            }
        }
    });
})();
       
$(function() {
    $('#player').youTubeChannel({user:'USUARIO_DEL_CANAL'});
});


Con esto queda pendiente solamente el HTML para la página:

<html>
    <head>
    </head>
    <body>
        <div id="player">
        </div>
    </body>
</html>


y el CSS para darle estilo:

#player {
    width: 348px;
    height: 240px;
    overflow: hidden;
    background: gray;
    position: absolute;
    border: solid 2px gray;
}

.youtube .carousel {
    width: 20%;
    height: 100%;
    overflow: auto;
    position: absolute;
    right: 0px;
    z-index: 3;
}

.youtube .thumbnail {
    margin: 2px;
    width: 80%;
    border: 1px solid black; 
}

.youtube iframe.player {
    width: 80%;
    height: 240px; 
    overflow: auto;
    border: 0;
}


Espero que sea de utilidad y si quieren dejar comentarios y sugerencias serán bienvenidas.

1 comentario:

  1. Primeiramente obrigado pelo código modificado, eu já vinha usando o código do Copyright 2011 : Simone Gianni só que na versão 2 e agora que tudo mudou para o v3 eu fiquei sem saber o que fazer e desde então estava procurando um código semelhante a esse que você escreveu.

    mesmo o seu código funcionando no fiddle corretamente, eu não consigo fazer que ele fique certo no meu site.
    tenho que ter um player com a lista de reprodução junto com o player só que isto não se sucede.
    me ajuda obrigado

    ResponderEliminar

El Blog de Nicolas Gomez J.

Blog dedicado a fotografía, café, libros, películas, Teoría General de Sistemas y sus aplicaciones en la administración, etc.

Seguidores