Автоматическое получение изображения с YouTube для миниатюры?
У меня есть пользовательский тип записи, который принимает HTML-код для вставки видео с YouTube, заголовок и изображение записи. В настоящее время я вручную ищу изображение для записи, но в идеале хотелось бы автоматически загружать и изменять размер первого кадра видео на основе URL видео в процессе сохранения записи. Если сделать это правильно, мой тип записи может требовать только ссылку, а изображение и код для вставки будут получены из нее.
Например, если ссылка на видео http://www.youtube.com/watch?v=2Jnpi-uBiIg, значение параметра v будет извлечено и использовано для загрузки изображения по адресу http://img.youtube.com/vi/2Jnpi-uBiIg/0.jpg.
Я совсем новичок в разработке для WordPress, но что-то подсказывает мне, что нужно использовать хуки (если я правильно понимаю их принцип работы).

Привет, @Jonathan Sampson:
Хотя это не совсем то, что ты просил, это может быть рабочим решением, и оно бесплатно предоставляется WordPress.com благодаря @yoast, который сегодня утром упомянул этот пост в блоге:
По сути, ты можешь использовать URL генератора скриншотов от WordPress.com в таком формате (и, согласно блогу, Мэтт вроде бы одобрил бесплатное использование):
https://s.wordpress.com/mshots/v1/{URL-кодированный-адрес}?w={ширина}
Возьмём твой URL из примера:
После URL-кодирования для использования с генератором скриншотов получится ссылка вида (для ширины 400px):
https://s.wordpress.com/mshots/v1/http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D2Jnpi-uBiIg?w=400
А вот как выглядит сам скриншот (я сохранил его для наглядности, поэтому он не изменится, даже если сервис WordPress обновится, вместо прямой ссылки на сервис для отображения изображения.) Непонятно, почему он захватил видео, а не всю страницу, но видео даже лучше для твоих целей:
(источник: mikeschinkel.com)
Конечно, при первом HTTP GET-запросе этого URL скриншот не вернётся, потому что сервера WordPress.com сначала должны его сделать, а это занимает слишком много времени для ожидания запроса. Поэтому при первом запросе ты получишь редирект на этот URL:
https://s.wordpress.com/wp-content/plugins/mshots/default.gif
И этот URL отображает такую картинку:
Но если подождать минуту после первого запроса и повторить его, то ты получишь свой скриншот. Я думаю, что лучше сначала запросить его, чтобы он кэшировался, подождать, скачать и сохранить локально на своём сервере, чтобы снизить нагрузку на сервера WordPress.com — вдруг они передумают предлагать это бесплатно (или, если трафика будет много, могут даже сделать платный API с дополнительными функциями!)
P.S. Кстати, чтобы доказать, что это действительно работает прямо на веб-странице, вот скриншот, запрошенный напрямую у WordPress.com. Он может отличаться от того, что я сохранил выше, или, если прошло много времени с момента последнего просмотра этой страницы, может даже отображаться картинка "Генерация превью". Если так, подожди минуту и обнови страницу — должно появиться:

Мой генератор постов для видеоблога (http://v.leau.co/) делает это, но не в контексте WordPress.
Вы вводите запрос, например "superman" (затем ждёте (без уведомлений о процессе, так как я единственный пользователь)), затем выбираете видео, которые хотите добавить в пост, нажимаете "сгенерировать код" и получаете код, где миниатюры размещены на моём сайте, так как они были загружены в процессе. Этот код можно скопировать и вставить в пост.
Другими словами, если поместить этот код в функцию, она вернёт фрагмент кода, например, ссылку на видео, которая добавляется к контенту, или ссылку на загруженное локально изображение для миниатюры.
Это тот код, который вам нужен? Я думаю, основное это:
Функция для получения дополнительных результатов (если вы хотите отобразить больше одного видео в результате вместо одного конкретного):
function retrieveMoreResults($key, $q, $start, $cache) {
$url = "http://ajax.googleapis.com/ajax/services/search/video?v=1.0&q=" . $q . "&rsz=large&start=" . $start. "&key=" . $key;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_REFERER, $referer);
$body = curl_exec($ch);
curl_close($ch);
$responseData = json_decode($body, true);
$tempOutputString .= display($responseData, $cache, $q);
return $tempOutputString;
}
Функция для получения начальной страницы результатов:
function retrieveResults($key, $q, $cache) {
# первый запрос
$url = "http://ajax.googleapis.com/ajax/services/search/video?v=1.0&q=" . $q . "&rsz=large&key=" . $key;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_REFERER, $referer);
$body = curl_exec($ch);
curl_close($ch);
$responseData = json_decode($body, true);
$tempOutputString = "";
$tempOutputString .= display($responseData, $cache, $q);
$boolFirstRequest = true;
foreach ($responseData["responseData"]["cursor"]["pages"] as $GsearchResultClass) {
$start = $GsearchResultClass["start"];
if ($boolFirstRequest) {
$boolFirstRequest = false;
} else {
$tempOutputString .= retrieveMoreResults($key, $q, $start, $cache);
}
}
return $tempOutputString;
}
Функция для загрузки миниатюр в определённую директорию (переменная) и возврата фрагмента кода для вставки в пост:
function display($responseData, $cache, $tag) {
$strBuffer="";
foreach ($responseData["responseData"]["results"] as $GsearchResultClass) {
#
# есть URL YouTube и Google Video, они различаются
# у Google Video в URL есть слово "ThumbnailServer"
# пример:
# YouTube: http://1.gvt0.com/vi/6jKzr143K8U/default.jpg
# Google Video: http://3.gvt0.com/ThumbnailServer2?app=vss&contentid=7efbd69963e4cc67&offsetms=30000&itag=w160&hl=en&sigh=J6N1fv_It6H5jJWX51fKt-eYqNk
#
$thumbId="";
$imageThumb=$GsearchResultClass["tbUrl"];
if (strstr($imageThumb, 'ThumbnailServer')) {
$imgStringBits = explode('&',$imageThumb);
$parsedImgStr=strstr($imgStringBits[1],'=');
$parsedImgStr = substr($parsedImgStr,1);
$thumbId = $parsedImgStr;
} else {
$imgStringBits = explode('/',$imageThumb);
$thumbId = $imgStringBits[4];
}
$imgFile=$cache . "/" . $thumbId . ".jpg";
#
# Проверяем, есть ли изображение в кэше:
# - если есть, НИКОГДА не удаляем его (зачем?)
# - если нет, загружаем через curl
#
if (!file_exists($imgFile)) {
$ch = curl_init ();
$timeout = 5;
curl_setopt ($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9) Gecko/2008052906 Firefox/3.0');
curl_setopt ($ch, CURLOPT_AUTOREFERER, true);
curl_setopt ($ch, CURLOPT_URL, $imageThumb);
curl_setopt ($ch, CURLOPT_HEADER, false);
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt ($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt ($ch, CURLOPT_VERBOSE, 1);
$rawdata = curl_exec ($ch);
curl_close ($ch);
if($rawdata) {
$fp = fopen($imgFile, 'w');
fwrite($fp, $rawdata);
fclose($fp);
} else {
#
# Если curl ничего не вернул, используем изображение по умолчанию
#
$imgfile="images/default.jpg";
}
}
#
# Создаём div (возможно, позже вынесу стили в отдельный CSS)
# содержащий миниатюру видео, ссылку и описание. Можно добавить
# другие параметры, возвращаемые Google, например длину видео и т.д.
#
$strBuffer .= "<div style=\"float:left;width:125px;height:130px;font-size:8px;font-family:arial;\" class=\"thumb\"><div>";
$strBuffer .= "<a href=\"#\" class=\"del\" id=\"$thumbId\"> ADD</a><br />";
$strBuffer .= "<a href=\"" . $GsearchResultClass["playUrl"] . "\" target=\"_blank\">";
$strBuffer .= "<img src=\"" . $imgFile . "\" alt=\"" . $GsearchResultClass["title"] . "\" border=\"0\" width=\"120\">";
$strBuffer .= "</a><br />\n";
#
# Также добавляем опцию удаления, пока что она только убирает видео со страницы,
# но в следующей версии должна делать AJAX-запрос, чтобы записать ID и больше
# не показывать это видео.
#
$strBuffer .= $GsearchResultClass["titleNoFormatting"] . "<br />";
$strBuffer .= "</div></div>\n";
}
return $strBuffer;
}
Функция для вызова вышеуказанного:
function moviePage($tag, $cacheName, $cacheTime, $key) {
$cache = $cacheName . "/" . $tag;
checkCache($cache);
cleanCacheHTML($cache);
$filename = $tag . ".html";
$spFile=$cache . "/" . $filename;
if (file_exists($spFile) && filemtime($spFile) > $cacheTime ) {
$strBuffer = file_get_contents($spFile) . "<!-- " . $spFile . " from cache -->";
} else {
$strBuffer = retrieveResults($key, $tag, $cache);
}
$strBuffer .= "<br clear=\"all\">";
$fp = fopen($spFile, 'w');
fwrite($fp, $strBuffer);
fclose($fp);
return $strBuffer;
}
($key - это ваш ключ Google API) (http://code.google.com/intl/nl-NL/more/)
На мой взгляд, остальное - это просто "взять возвращаемые данные и добавить их в контент поста + установить загруженную миниатюру как featured image".
P.S. На мой взгляд, всегда лучше добавлять миниатюру к видео с YouTube, так как старые видео часто удаляются, оставляя уродливые посты. Со своими миниатюрами изображение останется навсегда, и у вас будет представление о том, что было в посте изначально.

отличный ответ! А что если создать пользовательское поле для ввода исходного URL видео и загружать/связывать локальное изображение при сохранении записи? Последним шагом было бы удаление старого изображения и добавление/связывание нового при обновлении записи с новым URL. Если бы вы могли расширить свой код этим функционалом, я думаю, это было бы очень полезно для читателей.

Не уверен, что правильно понял, что именно вы имели в виду, но я нашел такое решение:
http://wpworks.wordpress.com/2010/12/23/display-youtube-thumbnail-with-wordpress-custom-field/
С наилучшими пожеланиями

Функция для отображения изображения на основе URL? Это то, что вы имели в виду?
function get_youtube_screen( $url = '', $type = 'default', $echo = true ) {
if( empty( $url ) )
return false;
if( !isset( $type ) )
$type = '';
$url = esc_url( $url );
preg_match("|[\\?&]v=([^&#]*)|",$url,$vid_id);
if( !isset( $vid_id[1] ) )
return false;
$img_server_num = 'i'. rand(1,4);
switch( $type ) {
case 'large':
$img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/0.jpg\" alt=\"Большое превью видео YouTube\" title=\"Превью видео YouTube\" />";
break;
case 'first':
// Миниатюра первого кадра
$img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/1.jpg\" alt=\"Первый кадр видео YouTube\" title=\"Первый кадр видео\" />";
break;
case 'small':
// Миниатюра одного из последующих кадров (неясно, как именно они определяют этот кадр)
$img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/2.jpg\" alt=\"Маленькое превью видео YouTube\" title=\"Превью видео\" />";
break;
case 'default':
case '':
default:
$img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/default.jpg\" alt=\"Стандартное превью видео YouTube\" title=\"Превью видео\" />";
break;
}
if( $echo )
echo $img;
else
return $img;
}
// Примеры вызова
get_youtube_screen( "http://www.youtube.com/watch?v=dZfmPREbTd8", 'default' );
get_youtube_screen( "http://www.youtube.com/watch?v=dZfmPREbTd8", 'large' );
get_youtube_screen( "http://www.youtube.com/watch?v=dZfmPREbTd8", 'small' );
get_youtube_screen( "http://www.youtube.com/watch?v=dZfmPREbTd8", 'first' );
YouTube, похоже, использует несколько серверов для миниатюр...
iN.ytimg.com
Где N обычно принимает числовое значение от 1 до 4 (иногда 5, но в тестах это было нестабильно).
Они также используют img.youtube.com
, но мне понравилась идея получения изображений с чередующихся серверов, поэтому я запрограммировал функцию для случайного выбора одного из 4 серверов.
ПРИМЕЧАНИЕ: Не для каждого видео доступны изображения всех размеров, некоторые могут отображаться пустыми, однако стандартное изображение, похоже, стабильно работает для всех протестированных URL видео.
Дайте знать, если это поможет...

Отличный ответ, +1. Это был замечательный вопрос; 4 совершенно разных ответа, и все кажутся очень правильными решениями. Я просто обожаю WA!

Я изучил класс embed и доступные фильтры, но не увидел простого способа передать изображение из объекта embed (который на самом деле содержит URL изображения) в функции шорткода, которые выводят HTML. Объект embed в настоящее время (насколько я смог увидеть) не передает необходимые данные через доступные фильтры/хуки. Не уверен, почему класс embed не передает информацию об изображении (которую он имеет), было бы логично это делать...
