Эта статья устарела, так как YouTube выпустил 3-ю версию своего API. Если вам нужно руководство по использованию версии 3, ознакомьтесь с этим постом .
YouTube Третий самый посещаемый ресурс в мире. Мы все его посетили и, возможно, загрузили на него видео. Но как мы, как разработчики Rails, можем получить информацию о видео на YouTube? Как мы можем отобразить их на нашем сайте? Есть ли библиотеки, которые могут помочь нам с этим? Давайте разберемся?
В этой статье я покажу вам, как создать веб-приложение, которое позволяет пользователям добавлять и просматривать видео с YouTube. После добавления информация о видео будет автоматически загружаться через API YouTube. Это приложение также будет отображать эти добавленные видео с помощью обычного проигрывателя YouTube с помощью API IFrame YouTube. Наконец, есть некоторые потенциальные проблемы и ошибки, с которыми вы можете столкнуться.
Исходный код доступен на GitHub .
Рабочую демонстрацию можно найти на Heroku .
Подготовка демонстрационного приложения
В этой статье я буду использовать Rails 3.2.17, но вы можете реализовать практически то же решение с Rails 4.
Начните с создания нового приложения Rails без набора тестов по умолчанию:
$ rails new yt_videos -T
Прежде всего, мы должны взять драгоценные камни, которые будут использоваться в этой демонстрации. Мы будем использовать youtube_it
, удобный гем, написанный Kyle J. Ginavan, для работы с API YouTube. Для стилизации давайте используем наш старый друг Twitter Bootstrap и, в частности, самоцвет bootstrap-sass
. Идите дальше и добавьте эти две строки в ваш Gemfile
:
Gemfile
gem 'bootstrap-sass', '~> 3.1.1.0' gem 'youtube_it', '~> 2.4.0'
Тогда беги
$ bundle install
Теперь добавьте
//= require bootstrap
в application.js
и
@import "bootstrap";
в application.css.scss
чтобы включить стили и скрипты Bootstrap. Конечно, в реальном приложении вы бы выбрали только необходимые компоненты.
Мы должны подумать о том, как будет выглядеть наша модель Video
. Конечно, он должен содержать ссылку на видео, добавленное пользователем. API YouTube предоставляет только уникальный идентификатор видео (который мы вскоре научимся извлекать), поэтому нам нужно его получить. Также мы хотим сохранить некоторую информацию о видео. Вот список всех атрибутов для нашей модели ActiveRecord:
-
id
— целое число, первичный ключ, индексированный, уникальный -
link
— строка -
uid
— строка. Также полезно добавить сюда индекс для обеспечения уникальности. Для этой демонстрации мы не хотим, чтобы пользователи добавляли одно и то же видео несколько раз. -
title
— строка Это будет содержать название видео, извлеченное из YouTube -
author
— строка. Имя автора извлечено из YouTube -
duration
— строка. Мы сохраним продолжительность видео в следующем формате: «00:00:00». -
likes
— целое число. Лайки рассчитывать на видео -
dislikes
— целое число. Не любит рассчитывать на видео.
Этого будет достаточно для целей этой демонстрации, но имейте в виду, что API YouTube позволяет получать намного больше информации.
Запустите эту команду, чтобы создать модель и соответствующую миграцию:
$ rails g model Video link:string title:string author:string duration:string likes:integer dislikes:integer
Теперь настройте маршруты:
routes.rb
resources :videos, only: [:index, :new, :create] root to: 'videos#index'
Нам не нужно edit
, update
, show
и destroy
действия для целей этой демонстрации. Также не забудьте удалить файл public/index.html
если вы работаете с Rails 3.
Последнее, что нужно сделать, это добавить блок для рендеринга флеш-сообщений в макет:
application.html.erb
[...] <div class="container"> <% flash.each do |key, value| %> <div class="alert alert-<%= key %>"> <button type="button" class="close" data-dismiss="alert">×</button> <%= value %> </div> <% end %> </div> [...]
Добавление видео
Когда пользователь посещает наш сайт, первое, что он, вероятно, должен увидеть, это кнопка «Добавить видео».
видео / index.html.erb
<div class="jumbotron"> <div class="container"> <h1>Share your videos with the world!</h1> <p>Click the button below to share your video from YouTube.</p> <p> <%= link_to 'Add video now!', new_video_path, class: 'btn btn-primary btn-lg' %> </p> </div> </div>
Благодаря Bootstrap этот блок будет выглядеть довольно красиво. Следующим шагом является реализация new
действия.
Но давайте остановимся на секунду и проверим файл models/video.rb
. Если вы используете Rails 3, все атрибуты (кроме id
, updated_at
и created_at
) стали доступны по умолчанию. Единственное, что нам нужно от пользователя — это ссылка на видео, которое он хочет добавить. Другие атрибуты будут заполнены автоматически, поэтому давайте внесем соответствующие изменения:
модели / video.rb
[...] attr_accessible :link [...]
Для Rails 4 вы должны разрешить только атрибут link
в контроллере:
params.require(:video).permit(:link)
Хорошо, теперь мы можем двигаться дальше. Добавьте эти два метода в контроллер:
Контроллеры / videos_controller.rb
[...] def new @video = Video.new end def create @video = Video.new(params[:video]) if @video.save flash[:success] = 'Video added!' redirect_to root_url else render 'new' end end [...]
Ничего необычного здесь не происходит.
Вид:
видео / new.html.erb
<div class="container"> <h1>New video</h1> <%= form_for @video do |f| %> <%= render 'shared/errors', object: @video %> <div class="form-group"> <%= f.label :link %> <%= f.text_field :link, class: 'form-control', required: true %> <span class="help-block">A link to the video on YouTube.</span> </div> <%= f.submit class: 'btn btn-default' %> <% end %> </div>
Как видите, мы только просим пользователя ввести ссылку на видео на YouTube. Здесь также отображается общая часть для отображения ошибок, которая выглядит следующим образом:
общий / _errors.html.erb
<% if object.errors.any? %> <div class="panel panel-danger"> <div class="panel-heading"> <h3 class="panel-title">The following errors were found while submitting the form:</h3> </div> <div class="panel-body"> <ul> <% object.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> </div> <% end %>
Настало время для забавной части — извлечения информации о видео, указанном пользователем. Мы собираемся сделать это внутри обратного вызова перед созданием записи.
модели / video.rb
before_create -> do # Our code here end
Чтобы получить информацию из API YouTube, нам понадобится уникальный идентификатор видео. На самом деле этот идентификатор уже присутствует в ссылке, которую предоставляет пользователь. Хитрость в том, что эти ссылки могут быть указаны в нескольких форматах, например:
- http://www.youtube.com/watch?v=0zM3nApSvMg&feature=feedrec grec index
- http://www.youtube.com/user/IngridMichaelsonVEVO#p/a/u/1/QdK8U-VIH_o
- http://www.youtube.com/v/0zM3nApSvMg?fs=1&hl=en_US&rel=0
- http://www.youtube.com/watch?v=0zM3nApSvMg#t=0m10s
- http://www.youtube.com/embed/0zM3nApSvMg?rel=0
- http://www.youtube.com/watch?v=0zM3nApSvMg
- http://youtu.be/0zM3nApSvMg
Все эти URL-адреса указывают на одно и то же видео с идентификатором 0zM3nApSvMg
(об этом можно прочитать в StackOverflow ).
Также стоит отметить, что uID видео содержит 11 символов. Чтобы получить uID из ссылки, мы будем использовать следующий RegExp:
/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/i
Прежде чем мы это сделаем, давайте удостоверимся, что у нас есть действующая ссылка:
модели / video.rb
YT_LINK_FORMAT = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/i validates :link, presence: true, format: YT_LINK_FORMAT [...]
Хорошо, загрузите uID внутри обратного вызова:
модели / video.rb
[...] before_create -> do uid = link.match(YT_LINK_FORMAT) self.uid = uid[2] if uid && uid[2] if self.uid.to_s.length != 11 self.errors.add(:link, 'is invalid.') false elsif Video.where(uid: self.uid).any? self.errors.add(:link, 'is not unique.') false else get_additional_info end end
Здесь есть несколько проверок для uid, особенно для проверки его длины и уникальности. Если все в порядке, вызовите метод для получения информации о видео:
модели / video.rb
[...] private def get_additional_info begin client = YouTubeIt::OAuth2Client.new(dev_key: 'Your_YT_developer_key') video = client.video_by(uid) self.title = video.title self.duration = parse_duration(video.duration) self.author = video.author.name self.likes = video.rating.likes self.dislikes = video.rating.dislikes rescue self.title = '' ; self.duration = '00:00:00' ; self.author = '' ; self.likes = 0 ; self.dislikes = 0 end end def parse_duration(d) hr = (d / 3600).floor min = ((d - (hr * 3600)) / 60).floor sec = (d - (hr * 3600) - (min * 60)).floor hr = '0' + hr.to_s if hr.to_i < 10 min = '0' + min.to_s if min.to_i < 10 sec = '0' + sec.to_s if sec.to_i < 10 hr.to_s + ':' + min.to_s + ':' + sec.to_s end
Это где youtube_it
вступает в игру. Сначала создается переменная client
— она будет использоваться для выдачи запросов. Вам может быть интересно: «Что такое ключ разработчика YouTube?». Этот ключ используется для выдачи запросов на общедоступные данные YouTube. Обратите внимание, что некоторые действия требуют авторизации пользователя, о которой вы можете прочитать здесь .
Чтобы получить ключ разработчика, зарегистрируйте новое приложение по адресу https://code.google.com/apis/console . Откройте его настройки, перейдите в «APIs & auth», затем «Credentials». Создайте новый открытый ключ для приложений браузера, который является вашим ключом разработчика.
После инициализации клиента загрузите видео с помощью video_by
, получив информацию о нем. Обратите внимание, что продолжительность видео представлена в секундах, поэтому мы должны реализовать метод parse_duration
чтобы отформатировать его так, как мы хотим.
На данный момент наше приложение позволяет пользователям добавлять свои видео. Он также извлекает некоторую информацию и обеспечивает проверку для пользовательского ввода. Хорошо, не правда ли?
Отображение видео
Хорошо, у нас есть видео, теперь для их отображения. Добавьте следующее к вашему контроллеру:
Контроллеры / videos_controller.rb
def index @videos = Video.order('created_at DESC') end
На взгляд:
видео / index.html.erb
[...] <% if @videos.any? %> <div class="container"> <h1>Latest videos</h1> <div id="player-wrapper"></div> <% @videos.in_groups_of(3) do |group| %> <div class="row"> <% group.each do |video| %> <% if video %> <div class="col-md-4"> <div class="yt_video thumbnail"> <%= image_tag "https://img.youtube.com/vi/#{video.uid}/mqdefault.jpg", alt: video.title, class: 'yt_preview img-rounded', :"data-uid" => video.uid %> <div class="caption"> <h5><%= video.title %></h5> <p>Author: <b><%= video.author %></b></p> <p>Duration: <b><%= video.duration %></b></p> <p> <span class="glyphicon glyphicon glyphicon-thumbs-up"></span> <%= video.likes %> <span class="glyphicon glyphicon glyphicon-thumbs-down"></span> <%= video.dislikes %> </p> </div> </div> </div> <% end %> <% end %> </div> <% end %> </div> <% end %>
#player-wrapper
— это пустой блок, где будет отображаться проигрыватель YouTube. Метод in_groups_of
группирует наши записи по 3 и отображает их в строках. Обратите внимание: если для формирования группы недостаточно элементов, Rails заменяет каждый отсутствующий элемент на nil
, поэтому мы должны добавить условие if video
.
Еще одна важная вещь, чтобы упомянуть, это изображение предварительного просмотра видео. Чтобы получить изображение для предварительного просмотра видео, используйте одну из следующих ссылок:
-
https://img.youtube.com/vi/mqdefault.jpg
— изображение размером 320 × 180 без черных полос выше и ниже изображения; -
https://img.youtube.com/vi/hqdefault.jpg
— изображение 480 × 360 с черными полосами выше и ниже изображения; -
https://img.youtube.com/vi/<1,2,3>.jpg
— изображение размером 120 × 90 с различными сценами из видео с черными полосами над и под картинкой.
Также при рендеринге изображений для предварительного просмотра видео мы сохраняем uID видео с использованием атрибута data-*
HTML5. Этот атрибут будет использован в ближайшее время.
Для отображения актуальных видео на нашем сайте будет использоваться API IFrame YouTube. Он создает проигрыватель YouTube с определенными параметрами, позволяя пользователю менять видео, приостанавливать и останавливать его и т. Д. Подробнее об этом можно прочитать здесь . Подключите необходимые файлы JavaScript, как это:
макеты / application.html.erb
[...] <script src="https://www.google.com/jsapi"></script> <script src="https://www.youtube.com/iframe_api"></script> </head> [...]
Теперь давайте напишем код CoffeeScript в новом файле yt_player.coffee
:
JavaScripts / yt_player.coffee
jQuery -> # Initially the player is not loaded window.ytPlayerLoaded = false makeVideoPlayer = (video) -> if !window.ytPlayerLoaded player_wrapper = $('#player-wrapper') player_wrapper.append('<div id="ytPlayer"><p>Loading player...</p></div>') window.ytplayer = new YT.Player('ytPlayer', { width: '100%' height: player_wrapper.width() / 1.777777777 videoId: video playerVars: { wmode: 'opaque' autoplay: 0 modestbranding: 1 } events: { 'onReady': -> window.ytPlayerLoaded = true 'onError': (errorCode) -> alert("We are sorry, but the following error occured: " + errorCode) } }) else window.ytplayer.loadVideoById(video) window.ytplayer.pauseVideo() return return
Прежде всего, мы инициализируем логическую переменную ytPlayerLoaded
которая проверяет, был ли загружен проигрыватель. После этого создайте функцию makeVideoPlayer
которая принимает один аргумент — uID видео — и создает проигрыватель YouTube или изменяет воспроизводимое видео. Если проигрыватель еще не загружен, мы добавляем новый блок #ytPlayer
к #player-wrapper
который в конечном итоге заменяется проигрывателем. После всего этого, наконец, создайте объект проигрывателя YouTube, назначив его ytplayer
(мы будем использовать его для вызова функций API).
Остановимся на секунду немного поговорим об аргументах, которые передаются при создании этого объекта. ytPlayer
— это идентификатор DOM блока, который должен быть заменен проигрывателем. Второй аргумент — это объект JavaScript, содержащий настройки для игрока:
-
width
— ширина плеера. Наш сайт имеет адаптивный макет, поэтому мы установили его на100%
-
height
— высота игрока. Мы должны рассчитать его на основе ширины. Типичное разрешение для YouTube составляет 16: 9, что означает соотношение 1,7 (7). -
videoId
—videoId
видео для загрузки -
wmode
— этот параметр управляет наслоением и прозрачностью объекта Flash ( подробнее ). Если мы не установим его наopaque
, проигрыватель будет перекрывать модальные окна (например, созданные с помощью jQuery UI) и выглядеть ужасно -
autoplay
— установите в1
, видео будет воспроизводиться при загрузке. В этой демонстрации мы не хотим, чтобы это произошло -
modestbranding
— если для этого параметра установлено значение1
, логотип YouTube не будет отображаться на панели управления, но все равно будет отображаться в правом верхнем углу, когда воспроизведение видео приостановлено. -
events
— здесь мы указываем два события для обработки. Когда проигрыватель загрузится (onReady), установите дляytPlayerLoaded
значениеtrue
. Если при загрузке плеера или видео произошла ошибка (например, это видео было удалено), предупредите пользователя.
Если проигрыватель уже был загружен, мы используем функцию loadVideoById
чтобы изменить воспроизводимое видео, а затем приостановить его, используя pauseVideo
(для демонстрационных целей).
Хорошо, наша функция готова. Мы хотим загрузить плеер и отобразить последнее видео, как только пользователь откроет наш сайт. Как нам этого добиться? Мы собираемся использовать специальную функцию, представленную Google API:
JavaScripts / yt_player.coffee
[...] _run = -> # Runs as soon as Google API is loaded $('.yt_preview').first().click() return google.setOnLoadCallback _run [...]
Этот google.setOnLoadCallback _run
означает, что функция _run
должна вызываться, как только API завершит загрузку. Если мы не используем этот обратный вызов и пытаемся загрузить проигрыватель, как только DOM будет готов, возникает ошибка, YT.Player
что YT.Player
не является функцией — это потому, что API еще не загружен.
Последнее, что нужно сделать, это привязать обработчик события click
к видео-превью:
JavaScripts / yt_player.coffee
[...] $('.yt_preview').click -> makeVideoPlayer $(this).data('uid') [...]
И это все! Теперь наши пользователи могут добавлять видео и смотреть их на нашем сайте.
На самом деле осталась одна маленькая проблема. Мы указали ширину проигрывателя в процентах, но высота проигрывателя указана в пикселях. Это означает, что если пользователь попытается изменить размер окна, плеер станет уже, но при этом он сохранит ту же ширину — это плохо. Чтобы решить эту проблему, мы можем привязать обработчик события resize
к window
следующим образом:
JavaScripts / yt_player.coffee
[...] $(window).on 'resize', -> player = $('#ytPlayer') player.height(player.width() / 1.777777777) if player.size() > 0 return [...]
Теперь рост игрока будет корректно изменен — продолжайте и попробуйте. Вы также можете отложить запуск этого события до тех пор, пока пользователь не прекратит изменение размера, иначе оно будет запущено постоянно. Для этого используйте библиотеку BindWithDelay, написанную Брайаном Гринстедом. Это супер просто:
JavaScripts / yt_player.coffee
[...] $(window).bindWithDelay('resize', -> player = $('#ytPlayer') player.height(player.width() / 1.777777777) if player.size() > 0 return , 500) [...]
С этой структурой мы откладываем запуск этого события на 500 мс.
Вывод
Это подводит нас к концу статьи. Мы говорили о геме youtube_it
который можно использовать для работы с API YouTube, и об API IFrame YouTube. Я надеюсь, что информация, представленная здесь, была полезной. Не забудьте поделиться своим мнением об этой статье в комментариях. До скорой встречи!