Neste post, vamos aprender como buscar um vídeo através da API do youtube. Iremos utilizar jQuery e knockoutjs.
Primeiro você deve ter uma acesso ao console da google: console google.
Já no console da google, você deve liberar acesso ao “YouTube Data API v3” em “APIs e autenticação”, e em seguida, criar um chave de acesso (vou deixar a minha chave de exemplo, mas peço a gentileza de gerar uma, que seja de seu acesso, em algum sistema de produção. De qualquer forma, vou deixar um campo, para informar sua chave de acesso, assim ficará fácil testar.
A primeira coisa que devemos fazer é: Criar a interface
<div class="container-fluid well" data-bind="submit: adicionarVideo"> <div class="container"> <form class="form-inline" role="form"> <div class="form-group"> <label>Chave youtube:</label> <input type="text" class="form-control" data-bind="value: keyYoutube" /> </div> <div class="form-group"> <label>Link do youtube:</label> <input type="text" class="form-control" data-bind="value: linkVideoYoutube" /> </div> <input type="submit" class="btn btn-danger" value="Adicionar" /> </form> </div> </div>
Acima estamos definindo um formulário com 2 campos: “Chave youtube” e “Link do youtube”. Repare que o form utiliza o bind handler “submit”, que chama o método “adicionarVideo”. E utilizamos os atributos “keyYoutube” e “linkVideoYoutube” da classe “ViewModel” respectivamente.
Em seguida devemos definir nossa classe “ViewModel”:
function ViewModel(){ var self = this; self.keyYoutube = ko.observable("AIzaSyAZMiYXuyOvi6SldCliycFeOJd2frt4wes"); self.linkVideoYoutube = ko.observable("https://www.youtube.com/watch?v=0FMfsT11pdA"); // http://stackoverflow.com/a/5831191 self.extrairVideoId = function (url) { var re = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w]*(?:['"][^<>]*>|<\/a>))[?=&+%\w-]*/ig; var result = re.exec(url); if (result) return result[1]; else return null; }; self.adicionarVideo = function(){ var url = "https://www.googleapis.com/youtube/v3/videos"; var videoId = self.extrairVideoId(self.linkVideoYoutube()); $.ajax(url, { data: { part: "snippet", id: videoId, key: self.keyYoutube() }, dataType: "jsonp", success: function(resultado){ var videos = resultado.items; if(videos.length > 0){ var video = videos[0]; console.log(video); } }, error: function(error){ console.log(error); } }); }; } ko.applyBindings(new ViewModel());
Acima temos uma classe chamada “ViewModel”, que tem dois atributos:
keyYoutube: É um atributo do tipo “observable”, que será responsável por armazenar a chave de acesso a API do youtube.
linkVideoYoutube: É um atributo do tipo “observable”, que ficará responsável por armazenar o link do vídeo, ao qual devemos buscar na API do youtube, identificado pelo “videoId”.
Também temos um método chamado “extrairVideoId”, ele é responsável por extrair o “videoId” do link do youtube. O mesmo faz uso de uma expressão regular. Eu utilizei uma dica de um usuário do stackoverflow, como segue: Dica de como extrair vídeo do youtube. Ele retorna somente a identificação que a API necessita para buscar o vídeo.
E por último, temos o método “adicionarVideo”, que por sua vez, envia um request do tipo “JSONP”. Ele envia como parâmetro a identificação do vídeo (extraído antes do request) e a chave de acesso a API. Nesse primeiro instante, estamos apenas mostrando o resultado do vídeo no console.
Veja essa primeira parte do exemplo: Primeiro exemplo.
Na próxima etapa vamos mostrar os vídeos que foram adicionados; Excluir o vídeo; Mostrar o thumb de tamanho médio do vídeo.
Primeiro, vamos mexer no html, e deixar dessa forma:
<div class="carregando" style="display: none" data-bind="visible: mostrarCarregando"></div> <div class="container-fluid well"> <div class="container"> <form class="form-inline" role="form" data-bind="submit: adicionarVideo"> <div class="form-group"> <label>Chave youtube:</label> <input type="text" class="form-control" data-bind="value: keyYoutube" /> </div> <div class="form-group"> <label>Link do youtube:</label> <input type="text" class="form-control" data-bind="value: linkVideoYoutube" /> </div> <input type="submit" class="btn btn-danger right" value="Adicionar" /> </form> </div> </div> <div class="container-fluid well" data-bind="visible: videosAdicionadosVisiveis"> <div class="container"> <div class="row" data-bind="foreach: videos"> <p data-bind="text: titulo"></p> <div class="col-xs-6 col-md-3 item-video"> <a class="btn btn-default btn-filter right link-fechar" data-bind="click: $root.removerVideo"> <span class="glyphicon glyphicon-remove"></span> </a> <a href="#" class="thumbnail" data-bind="attr: {href: linkVideo}" target="_blank"> <img data-bind="attr: {alt: titulo, src: imagemMedia}" /> </a> </div> </div> </div> </div>
Na primeira linha adicionamos uma div com a classe carregando (abaixo tem o css que formatada a mesma), para mostrar um carregando enquanto a busca na API do youtube está ocorrendo. E utilizamos o bind handler “visible” para definir se o elemento deve ser visível ou não, na prática, mostraremos a div antes de começar a consulta e esconderemos sempre que terminar.
O html que segue é o mesmo do primeiro passo, então vamos pular esse bloco e ir para o segundo. Onde temos uma div que engloba os vídeos. Repare que utilizando o “visivel” nessa div, com uma propriedade “observable” “videosAdicionadosVisiveis”. Essa div só deve ficar visível no caso do usuário ter adicionado ao menos um vídeo. Um detalhe que considero importante. Parece simples verificar o tamanho do array de videos ali no html mesmo, em vez de colocar em um “computed” do KO. Porem, as regras mudam, e nesse caso, isso pode virar uma dor de cabeça. Ter que mudar em todos os lugares que essa regra é utilizada.
Mais abaixo temos uma definição de um “foreach” para listar os videos da propriedade “videos”, que nada mais é do que um “observableArray”. Para cada item do vídeo, temos um título. Um link, para remover o vídeo, que por sua vez, chama o método “removerVideo”, e que se encontra na raiz da classe “ViewModel”, por isso, devemos declarar ele com “$root”. Também temos um link que atribui um link para o vídeo no youtube, através da propriedade “linkVideo”. E dentro do link, temos uma imagem, que corresponde ao thumb de tamanho médio da imagem do vídeo, na propriedade “imagemMedia”. E por último, declaramos o “alt” da imagem, com o título do vídeo.
Agora devemos ver como vai ficar a classe “ViewModel”.
function VideoViewModel(titulo, youTubeId, imagemMedia){ var self = this; self.titulo = titulo; self.youTubeId = youTubeId; self.imagemMedia = imagemMedia; self.linkVideo = "https://www.youtube.com/watch?v=" + youTubeId; } function ViewModel(){ var self = this; self.keyYoutube = ko.observable("AIzaSyAZMiYXuyOvi6SldCliycFeOJd2frt4wes"); self.linkVideoYoutube = ko.observable("https://www.youtube.com/watch?v=0FMfsT11pdA"); self.videos = ko.observableArray(); self.mostrarCarregando = ko.observable(false); self.videosAdicionadosVisiveis = ko.computed(function(){ return self.videos().length > 0; }); // http://stackoverflow.com/a/5831191 self.extrairVideoId = function (url) { var re = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w]*(?:['"][^<>]*>|<\/a>))[?=&+%\w-]*/ig; var result = re.exec(url); if (result) return result[1]; else return null; }; self.adicionarVideo = function(){ if(!self.linkVideoYoutube()) return; var url = "https://www.googleapis.com/youtube/v3/videos"; var videoId = self.extrairVideoId(self.linkVideoYoutube()); var buscaVideo = ko.utils.arrayFirst(self.videos(), function(video){ return video.youTubeId == videoId; }); if(buscaVideo) return; self.mostrarCarregando(true); $.ajax(url, { data: { part: "snippet", id: videoId, key: self.keyYoutube() }, dataType: "jsonp", success: function(resultado){ var videos = resultado.items; if(videos.length > 0){ var video = videos[0]; var videoViewModel = new VideoViewModel(video.snippet.title, videoId, video.snippet.thumbnails.medium.url); self.videos.push(videoViewModel); self.linkVideoYoutube(""); } }, error: function(error){ console.log(error); } }).always(function(){ self.mostrarCarregando(false); }); }; self.removerVideo = function(video){ self.videos.remove(video); }; } ko.applyBindings(new ViewModel());
Vamos analisar em partes. Primeiro definimos uma nova classe, chamada “VideoViewModel”. Nessa temos os atributos:
titulo: Conterá o título do vídeo.
youTubeId: Identificação do vídeo no youtube.
imagemMedia: Url da imagem, utilizaremos a imagem de tamanho média.
linkVideo: Link para visualizar o vídeo no youtube.
Repare que não definimos nenhum atributo como “observable”, partindo do principio, que não temos necessidade de alterar essas informações, não há necessidade de colocar como “observable”, só acarretaria em custo de processamento, no lado do cliente.
Já na classe “ViewModel”, adicionamos algumas propriedades novas:
videos: Está será um array, que armazenará os vídeos adicionados pelo usuário.
mostrarCarregando: Propriedade que define se o carregando deve ficar visível ou não.
videosAdicionadosVisiveis: Propriedade computada, que retorna um “bool”, “true” caso o quantidade de vídeos adicionados seja superior que zero, e “false” do contrário.
Alteramos o método “adicionarVideo”. Primeiro verificamos se a propriedade “linkVideoYoutube” é “null”, caso essa condição seja verdadeira, não damos continuidade com o algoritmo. Assim não tentaremos buscar vídeo sem link.
Mais abaixo, declaramos a variável “buscaVideo”, ela armazena o resultado da busca de um vídeo. Ao qual o usuário está tentando adicionar. Caso o vídeo já esteja adicionado na propriedade “videos”, o algoritmo deve parar por ali.
Logo abaixo definimos a propriedade “mostrarCarregando” como “true”, assim mostraremos o carregando na tela.
Executamos o ajax como anteriormente, a diferença da primeira etapa para esta, é que mudamos a implementação de “success”, e adicionamos uma função há “always”, para sempre esconder o “carregando” (independente de ocorrer sucesso ou falha, na requisição).
Na implementação de “success”, definimos uma variável “videos”, que tem um array de vídeos (providos da API). Em seguida, verificamos se o tamanho desse array é superior a zero. Caso seja superior, atribuímos o valor do primeiro item do array a variável “video”.
Na linha de baixo, criamos uma instancia da classe “VideoViewModel”, pegamos o valores que correspondem a ordem no construtor da classe (título, identificação do vídeo e url da imagem de tamanho médio).
Com a variável instanciada, adicionamos um novo item no array “videos”, através do método “push”.
E por fim, definimos o valor de “linkVideoYoutube” como vazio, assim, o link do youtube no campo sumirá.
Também adicionamos um método para excluir um vídeo, chamado “removerVideo”. Ele apenas remove o vídeo do array “videos”, que foi passado como parâmetro no método.
E por último, definimos o css, para formatar alguns itens na página.
.carregando{ width:100%; height:100%; position: fixed; top: 0; bottom: 0; left: 0; right: 0; z-index: 999; -moz-opacity: .6; -webkit-opacity: .6; filter: alpha(opacity = 60); opacity: .6; background: #000 } .item-video{ position: relative } .link-fechar{ position: absolute; left: 250px; top: 7px; -moz-opacity: .6; -webkit-opacity: .6; filter: alpha(opacity = 60); opacity: .6; } .right{ float: right }
Acima definimos a formatação do link que excluir o vídeo; Do carregando da página e de cada item do vídeo.
Veja o exemplo funcionando Exemplo final.
Para mais detalhe da API do youtube, consulte API do youtube.
Até a próxima pessoal!