¿Cómo obtener automáticamente la imagen de YouTube para la miniatura?

27 nov 2010, 05:01:31
Vistas: 8.77K
Votos: 6

Tengo un tipo de entrada personalizado que acepta un fragmento HTML de inserción de YouTube, un título y una imagen destacada. Actualmente, estoy buscando manualmente una imagen para la imagen destacada, pero idealmente me gustaría poder descargar y redimensionar automáticamente el primer fotograma del video basado en la URL del video durante el proceso de guardado de la entrada. Si se hace correctamente, mi tipo de entrada podría requerir solo el enlace y obtener tanto la imagen como el código de inserción a partir de ese enlace.

Por ejemplo, si el enlace del video es http://www.youtube.com/watch?v=2Jnpi-uBiIg, se extraería el valor de v y se usaría para descargar la imagen en http://img.youtube.com/vi/2Jnpi-uBiIg/0.jpg.

Soy muy nuevo en el desarrollo de WordPress, pero algo me dice que debería investigar sobre hooks (si es que los entiendo correctamente).

3
Comentarios

Intentaría usar y extender oEmbed para esto, en lugar de un fragmento de HTML... Pero después de pasar un tiempo revisando ese código en el núcleo - este consejo a una persona nueva en WP se siente como tirarlo debajo del autobús. :)

Rarst Rarst
27 nov 2010 15:38:42

Solo tengo mucha curiosidad si mi versión funcionó para ti..?

MikeSchinkel MikeSchinkel
24 dic 2010 15:10:44

@Mike Tiempo ocupado del año - necesitaré volver a ti :) Agradezco la información y revisaré esta publicación pronto para leer todas las soluciones de nuevo en un futuro cercano.

Sampson Sampson
24 dic 2010 18:46:38
Todas las respuestas a la pregunta 4
1

Hola @Jonathan Sampson:

Aunque esto no es exactamente lo que pediste, podría ser una solución viable y es completamente gratuita gracias a WordPress.com, mencionado por @yoast esta mañana en un tweet donde hizo referencia a esta publicación de blog:

Básicamente, puedes usar el generador de capturas de pantalla de WordPress.com con una URL en este formato (según la publicación de blog, Matt pareció aprobar su uso gratuito):

  • https://s.wordpress.com/mshots/v1/{URL-codificada}?w={ancho}

Tomando tu URL de ejemplo:

Y luego codificándola para usarla con el generador de capturas, obtienes una URL como esta (para un ancho de 400px):

https://s.wordpress.com/mshots/v1/http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D2Jnpi-uBiIg?w=400

Y, por supuesto, lo que sigue es cómo se ve la captura (copié la imagen para conservarla, así que no cambiará aunque el servicio de WordPress se modifique, en lugar de enlazar directamente al servicio para mostrar la imagen.) No estoy seguro de por qué capturó el video y no toda la página, pero el video es incluso mejor para lo que necesitas:

Captura de pantalla de un video de Youtube generada por el servicio de WordPress
(fuente: mikeschinkel.com)

Por supuesto, la primera vez que hagas una petición HTTP GET a la URL, no obtendrás la captura porque los servidores de WordPress.com primero necesitan generarla, lo cual toma demasiado tiempo para mantener la conexión abierta. En la primera petición, serás redirigido a esta URL:

  • https://s.wordpress.com/wp-content/plugins/mshots/default.gif

Y esa URL muestra esta imagen:

Generando vista previa - Imagen de WordPress.com

Pero si esperas un minuto después de la primera petición y vuelves a intentarlo, obtendrás la captura. Mi recomendación es que la solicites para almacenarla en caché, esperes a descargarla y luego la guardes localmente en tu servidor para reducir la carga en los servidores de WordPress.com y evitar que reconsideren ofrecer este servicio gratis (¡o incluso podrían convertirlo en un servicio de pago con API premium!).

P.D. Por cierto, para demostrar que funciona dentro de una página web, aquí está la captura solicitada directamente desde WordPress.com. Ten en cuenta que puede ser diferente a la imagen que guardé y enlacé arriba, o si ha pasado tiempo desde que alguien visitó esta página y su caché se ha limpiado, incluso podría mostrar la imagen de "Generando vista previa". Si es así, espera un minuto y actualiza la página para ver el resultado:

Captura de pantalla de un video de Youtube directamente desde el servicio de WordPress.com

23 dic 2010 14:13:48
Comentarios

Eso parece útil.. :)

t31os t31os
23 dic 2010 16:06:22
2

Mi generador de entradas de video blog (http://v.leau.co/) hace esto pero no en contexto de WordPress.

Proporcionas una consulta, por ejemplo "superman" (luego esperas (sin aviso de que está haciendo algo) (ya que soy el único usuario)), luego haces clic en los videos que te gustan para publicar, haces clic en generar código y obtienes el código donde las miniaturas están alojadas en mi sitio porque las descargó mientras tanto. Este código se puede copiar y pegar en una publicación.

En otras palabras, si colocaras el código en una llamada de función, devolvería el fragmento de código, por ejemplo, un enlace href con un enlace al video que se agrega al contenido o, por ejemplo, el enlace a la imagen destacada que se descarga localmente.

¿Es ese el fragmento de código que estás buscando? Creo que el núcleo es:

Una función para recuperar más resultados (es decir, si solo deseas mostrar más de 1 video en el código resultante en lugar de 1 específico):

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;
}

Una función para obtener la página de resultados inicial:

function retrieveResults($key, $q, $cache) {    
# primera llamada
$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;
}

Una función para mostrar y descargar las miniaturas en un directorio determinado (variable) y devolver un fragmento de código para poner en la publicación:

function display($responseData, $cache, $tag) {
$strBuffer="";

foreach ($responseData["responseData"]["results"] as $GsearchResultClass) {

    #
    # hay URLs de YouTube y también URLs de Google Video, ambas son diferentes
    # la de Google video tiene la palabra "ThumbnailServer" en ella
    # ejemplo:
    # youtube: http://1.gvt0.com/vi/6jKzr143K8U/default.jpg
    # video.google: 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";

    #
    # Ahora que tenemos el nombre del archivo de imagen, verificamos si ya lo tenemos en la caché:
    # - si lo tenemos, NUNCA lo eliminamos (¿por qué deberíamos?)
    # - si no lo tenemos... lo obtenemos mediante 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 {
            #
            # Si nuestra acción Curl no recupera nada, usamos la imagen predeterminada
            #
            $imgfile="images/default.jpg";
        }
    }

    #
    # Ahora que tenemos la URL de la imagen, creamos un div (hmm.. podría dejar eso
    # de estilo más adelante en un CSS separado) que contenga la miniatura del video, el enlace
    # y la descripción. Si te gusta, puedes agregar todos los demás
    # parámetros que devuelve Google, como la duración, etc...
    #
    $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\"> AÑADIR</a><br />";                
    $strBuffer .=  "<a href=\"" .  $GsearchResultClass["playUrl"]  .    "\" target=\"_blank\">";
    $strBuffer .=  "<img src=\"" . $imgFile . "\" alt=\"" . $GsearchResultClass["title"] . "\" title=\"Miniatura de video\" border=\"0\" width=\"120\">";
    $strBuffer .=  "</a><br />\n";
    #
    # Nota que también agregamos una opción de eliminación, por ahora solo lo elimina de la página
    # pero en la próxima versión debería hacer una llamada AJAX para escribir el ID en algún lugar
    # para que nunca lo volvamos a ver.
    #
    $strBuffer .= $GsearchResultClass["titleNoFormatting"] . "<br />";
    $strBuffer .= "</div></div>\n";
}
return $strBuffer;
}

Una función para llamar a lo anterior:

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 . " desde caché -->";
} else {                    
    $strBuffer = retrieveResults($key, $tag, $cache);
}
$strBuffer .=  "<br clear=\"all\">";
$fp = fopen($spFile, 'w');
fwrite($fp, $strBuffer);
fclose($fp);

return $strBuffer;  
}

(la $key es tu clave de API de Google) (http://code.google.com/intl/es/more/)

En mi humilde opinión, creo que el resto es más solo "obtener lo devuelto y agregarlo al contenido de una publicación + establecer la miniatura en caché descargada como destacada".

PD: En mi humilde opinión, siempre es mejor publicar una miniatura en un video cuando se hace referencia a videos de YouTube, ya que a menudo YouTube elimina videos antiguos y deja publicaciones feas como resultado. Con tus propias miniaturas, al menos la imagen permanece allí para siempre, por lo que después tienes una pista de lo que publicaste originalmente.

28 nov 2010 17:30:03
Comentarios

¡gran respuesta! ¿Qué tal si creas un campo personalizado para que el usuario ingrese la URL de origen del video y que ejecute la descarga de la imagen/enlace local de la imagen al guardar la publicación? Un paso final sería que cuando actualices la publicación con una nueva URL, la imagen anterior se elimine y se agregue/enlace la nueva. Si pudieras extender tu código con esto, creo que sería muy útil para los lectores.

NetConstructor.com NetConstructor.com
29 nov 2010 19:53:59

sí... pero estoy demasiado ocupado lol, en 2 semanas tengo vacaciones...

edelwater edelwater
29 nov 2010 22:43:59
1

No estoy seguro de haber entendido exactamente lo que quieres decir, pero encontré esta solución alternativa:

http://wpworks.wordpress.com/2010/12/23/display-youtube-thumbnail-with-wordpress-custom-field/

Saludos cordiales

23 dic 2010 13:34:33
Comentarios

@Alvaro Neto - Eso es realmente genial, y no es algo que supiera. Lástima que solo parecen tener miniaturas pequeñas con ese método.

MikeSchinkel MikeSchinkel
23 dic 2010 14:53:21
2

¿Función para mostrar la imagen basada en la URL? ¿Es esto lo que tenías en mente?

/**
 * Obtiene la miniatura de un video de YouTube basado en la URL
 * 
 * @param string $url URL del video de YouTube
 * @param string $type Tipo de miniatura ('default', 'large', 'small', 'first')
 * @param bool $echo Si es true, imprime la imagen directamente
 * @return string|bool Retorna el HTML de la imagen o false si hay error
 */
function get_youtube_screen( $url = '', $type = 'default', $echo = true ) {
    if( empty( $url ) )
        return false;

    if( !isset( $type ) )
        $type = '';

    $url = esc_url( $url );

    // Extraer el ID del video de la URL
    preg_match("|[\\?&]v=([^&#]*)|",$url,$vid_id);

    if( !isset( $vid_id[1] ) )
        return false;

    // Seleccionar un servidor de imágenes aleatorio
    $img_server_num =  'i'. rand(1,4);

    // Determinar la imagen según el tipo solicitado
    switch( $type ) {
        case 'large':
            $img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/0.jpg\" alt=\"Miniatura grande del video de YouTube\" title=\"Miniatura del video\" />";
            break;
        case 'first':
            // Miniatura del primer fotograma
            $img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/1.jpg\" alt=\"Primer fotograma del video de YouTube\" title=\"Primer fotograma\" />";
            break;
        case 'small':
            // Miniatura de un fotograma posterior
            $img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/2.jpg\" alt=\"Miniatura pequeña del video de YouTube\" title=\"Miniatura pequeña\" />";
            break;
        case 'default':
        case '':
        default:
            $img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/default.jpg\" alt=\"Miniatura por defecto del video de YouTube\" title=\"Miniatura del video\" />";
            break;
    }
    
    if( $echo )
        echo $img;
    else
        return $img;
}

// Ejemplos de uso
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 parece servir las imágenes desde varios servidores para las miniaturas...

iN.ytimg.com

Donde N es usualmente un valor numérico del 1 al 4 (a veces 5, pero no fue consistente en las pruebas).

También usan img.youtube.com pero me gustó la idea de obtenerlas de servidores alternos, así que programé la función para elegir aleatoriamente entre 1 de 4 servidores para mostrar la imagen.

NOTA: No siempre hay una imagen para cada tamaño en cada video, algunas pueden aparecer en blanco, sin embargo la imagen por defecto parece funcionar consistentemente en las URLs de video que probé.

Déjame saber si esto ayuda...

23 dic 2010 19:04:53
Comentarios

Buena respuesta, +1. Esta fue una gran pregunta; 4 respuestas completamente diferentes y todas parecen soluciones muy válidas. ¡Simplemente amo WA!

MikeSchinkel MikeSchinkel
24 dic 2010 15:19:14

Revisé la clase embed y los filtros disponibles, pero no pude encontrar una manera fácil de llevar la imagen desde el objeto embed (que de hecho contiene la URL de la imagen) a las funciones de shortcode que generan el HTML, el objeto embed actualmente (hasta donde pude ver) no pasa los datos necesarios a los filtros/hooks disponibles. No estoy seguro de por qué la clase embed no pasa la información de la imagen (que tiene), tendría sentido que lo hiciera...

t31os t31os
27 dic 2010 14:43:35