Учим wordpress (и не только) отрисовывать быстро Youtube плееры

  • Tutorial
youtube-speedload

Я вспомнил сайт одного старого приятеля-программиста, у которого в ленте сайта один хип-хоп, лет 6 назад он плевался на скорость загрузки страниц: «да-да-да, надо переделать, да ничего сложного там нет...» а вот зашел сейчас — все по-старому :-) Не смотря на простые технические решения, допускаю, что не только у меня есть такой приятель. Поэтому эта маленькая техническая заметка.

Чтобы понимать, о какой проблеме мы говорим:
image

400kb скрипта base.js?

Остальное даже не смотрю.
После оптимизации даже без сжатия выигрышь будет в десяки раз и составит:

html — 0.5 kb
css — 1.4 kb
js — 0.2 kb

Мне же и самому приходилось часто публиковать лекции с нашего YouTube канала в ленте сайта, их накаливалось на одной страничке достаточно много. Да и при просмотре личных блогов встречаются подборки чужих или своих видео. И что меня нервировало, то как youtube кладет на производительность при встраивании. Чего там только не грузится через iframe ради той же картинки с кликом. И самое забавное, я просто может и не хочу смотреть видео, нафига мне грузить контент?

Эта проблема давно давным давно решена в социальных сетях и множестве других ресурсов с большой посещаемостью. Да и в мобильных браузерах. Ни для кого уже не секрет, что пользователь больше времени проводит на сайте, если он максимально быстро получает контент.

Вот тот же хабр, у него не так много попадается в ленте видео, поэтому это не столь востребовано. Однако, если популяризировать этот подход?

Давайте попробуем реализовать это на примере плагина для WordPress.

Как держатель пары сайтов на wordpress я прекрасно знаю, как авторы плагинов умеют гадить в базу. Поэтому я точно решил, что плагин будет использовать нативный механизм кеширования и не будет ничего требовать и ломать совместимость, если его удалить.

Что можно вытащить из Youtube


Youtube для встраивания контента использует oembed. Подробнее мой ответ на SO о том, что можно вытащить, без обращения к официальному API.

stackoverflow.com/questions/10066638/get-youtube-information-via-json-for-single-video-not-feed-in-javascript/23253789#23253789

Ответ в JSON
www.youtube.com/oembed?url=http://www.youtube.com/watch?v=ojCkgU5XGdg&format=json

Или xml
www.youtube.com/oembed?url=http://www.youtube.com/watch?v=ojCkgU5XGdg&format=xml

Типы превьюшек разных размеров hq, sd, maxres
img.youtube.com/vi/ojCkgU5XGdg/hqdefault.jpg
img.youtube.com/vi/ojCkgU5XGdg/sddefault.jpg
i.ytimg.com/vi/ojCkgU5XGdg/maxresdefault.jpg
img.youtube.com/vi/ojCkgU5XGdg/0.jpg
img.youtube.com/vi/ojCkgU5XGdg/1.jpg
img.youtube.com/vi/ojCkgU5XGdg/2.jpg
img.youtube.com/vi/ojCkgU5XGdg/3.jpg

Аннотации к видео
www.youtube.com/annotations_invideo?cap_hist=1&video_id=ojCkgU5XGdg

Еще одна ссылка
www.youtube.com/get_video_info?html5=1&video_id=ojCkgU5XGdg

А это вам пригодится, если вы делаете стримы.
www.youtube.com/embed/live_stream?channel=UCkA21M22vGK9GtAvq3DvSlA

Превью прямых трансляций
i.ytimg.com/vi/W-fSCPrYSL8/hqdefault_live.jpg

Жаль, что YT не поддерживает jsonp. Поэтому полностью отказаться от сохранения данных у нас не получится.

Как вордпресс кеширует oembed


WordPress поддерживает автовстраивание при нахождении ссылок в редакторе из белого списка. При срабатывании этого события он создает мета-поле к посту с префиксом _oembed_, а при выводе — подменяет на html код из кеша.

Если мы решили показывать верстку с подстановкой iframe по клику, то нам необходимо где-то хранить это состояние, лучше всего в том же кеше.

Фильтр add_filter('embed_oembed_html'); позволяет до вывода поста подменить кеш. Он представляет из себя всем известный iframe.

<iframe width="" height=""  src=""></iframe>


Так как пока разбираются, почему гугл не формирует title для своих iframe (ticket #4024) мы, увы, вынуждены делать разово запрос на сервер для захвата title в кеш.

Как обновить кеш своими данными


add_filter('embed_oembed_html', 'ytsl_oembed_html', 1, 3);
ytsl_oembed_html($cache, $url, $attr){};


В момент срабатывания фильтра, я ищу в переменной $cache наличие моего тега data-ytsl. Если его нет, то делаю запрос на

http://www.youtube.com/oembed?url=http://www.youtube.com/watch?v=ojCkgU5XGdg&format=json


и формирую содержание нового тега. В итоге кеш выглядит вот так:

<iframe width="" height=""  src=" " data-ytsl=" "></iframe>


Обновление кеша делается так. Сначала считаем ключ
$cachekey   = '_oembed_' . md5( $url . serialize( $attr ) );

А потом обновляем.
update_post_meta( get_the_ID(), $cachekey, $cache );


Если же мой тег data-ytsl обнаруживается, я вытаскиваю из него title и id и формирую html.
В data-iframe я вставляю уже чистый код кеша без моего тега, для того чтобы по клику подменить этим содержанием верстку.

<div class='ytsl-click_div' data-iframe='$ytsl' style='$fixed position:relative;background: url($thumb_url) no-repeat scroll center center / cover' >
    <div class='ytsl-title_grad'>
        <div class='ytsl-title_text'>{$json['title']}</div>
    </div>
    <div class='ytsl-play_b'></div>
</div>"

Css -ка на гитхабе, тут нет смыла ее показывать.

В итоге плеер выглядит вот так.


В качестве отличия, я сделал кнопку play другого цвета. А вот то что тайтл написал Arial, а не кастомным шрифтом, который тоже грузится через iframe, по-моему, ни сколько не влияет на узнаваемость.

Итоговое решение обработки «на лету» очень скоростное, и судя по плагину профайлеру P3 от goDaddy обходится совершенно безболезненно. Поэтому удалось найти баланс между удобством и скоростью. Плагин вообще не требует от пользователя никаких действий. Он просто показывает картинки вместо iframe когда включен.

На фронтенде.

Всего лишь подключаем скрипт. Все до безобразия элементарно:

(function(){
  var f = document.querySelectorAll(".ytsl-click_div");
  for (var i = 0; i < f.length; ++i) {
       f[i].onclick = function () {
	 this.parentElement.innerHTML = this.getAttribute("data-iframe");
      }
    }
})();


Минусы — на мобильных устройствах браузеры шибко умные, и умеют делать это самостоятельно. Поэтому если не делать определение мобильных устройств, для воспроизведения придется делать по два клика.

Итого:
html — 0.5 kb
css — 1.4 kb
js — 0.2 kb
font — 0 kb
img — 50 kb (зависит от фоновой картинки)

Остальных минусов не заметил за довольно большой промежуток времени.

Ну а кота нарисовал в нагрузку за не столь длинный, и не столь технически интересный рассказ.

Код лежит тут github.com/Alexufo/youtube-speedload

Комментарии 10

    0
    Достаточно в src iframe yuotube прописать параметр controls=2.
      0
      И получить вместо 2 киллограмов скриптов один вместе с теми же шрифтами и аналитикой. А требуется для этого всего-то картинка, кнопка и title. К тому же при controls=2 он не интерактивен при наведении.
        0
        _http://jpegshare.net/images/41/7d/417dd3a30a9da315a4cdedd7378530c7.png

        Загружена страница форума с 4-мя видео (включен controls=2). Что загрузилось с youtube:
        html — 57.51 кбайт
        css — 49.52 кбайт
        js — 47.64 кбайт
        font — 25.09 кбайт
        img — 82.78 кбайт

        Аналитики до активации воспроизведения видео нет.
        Загрузка страницы заняла меньше 3 секунд на АДСЛ 2 мбита/сек.
          +2
          В случае самостоятельного решения вопроса у вас получится так:

          html — 0.5 kb
          css — 1.4 kb
          js — 0.2 kb
          font — 0 kb
          img — 50 kb (зависит от фоновой картинки)

          Если выкинуть из расчетов картинки, то у вас ресурсов 179,76кб / 4 = 44,94kb на ролик против 2,1kb. Разница в 20 раз. Поэтому если кто хочет немного подпилить, результат не заставит ждать себя от одного ролика и более.
            0
            Разница в 20 раз.

            css, js и font загружаются единожды при загрузке страницы.
              +3
              Запросы на кешируемые файлы постоянные, на html постоянные, работа 50 кб скрипта на каждый плеер постоянная (утверждать точно не берусь).

              В самостоятельном решении у вас только 1 необходимый запрос на сервер для загрузки бекграунда. Нет ни одного аргумента, зачем грузить дольше, если можно в разы меньше. Ровно так и реализовано в каждой соц. сети и других популярных сервисах.
      +2
      Решая эту задачу разработчик должен ответить себе на вопрос — нужны ли мне зафиксированные видео-сервисом просмотры с моей страницы?

      1) Если ответ нет (меня не волнуют зафиксированные сервисом просмотры) — то правильное решение это сохранение в кеше превшьюшки от видео, генерация на странице виджета с закешированным изображением которое дает пользователю однозначно понять что это видео, и генерация правильного iframe на лету в момент клика по виджету, с параметром автостарт

      2) Если ответ да (для меня важны зафиксированные просмотры) — задаться вопросом, приемлемо для меня если пользователь должен будет сделать повторный клик. Если ответ да — то выбирается решение номер один из которого исключается автостарт. По первому клику происходит замена виджета ифреймом.

      3) И только в самом последенем случае в страницу вставляется реальный iframe от сервиса.

      Или использование более продвинутых техник подмены виджета iframe мом в зависимости от активности пользователя. НО изначальный посыл в том, что если у вас страница с большим количеством таких фреймов, то страницу нужно верстать без них, и генерировать код iframe на лету в момент когда он действительно нужен пользователю.
        0
        По пункту 2. Повторный клик не нужен, подставляется в параметр в src &autoplay=1 и все работает с 1 клика. Повторный клик делается на мобильных браузерах, т.к те детектят фреймы с видео.
          +1
          Повторный клик НУЖЕН. Потому что тот же ютьб, вставленный ифрейм с автостартом может не считать за просмотр. ПУнкт два нужен именно для того, чтобы гарантированно учесть все просмотры. При автостарте с вставленным фремом никакой гарантии этому нет, о чем сообщает сам сервис
            0
            ааа… понял понял. В этом смысле да.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое