Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I have a custom post type that accepts a YouTube embed HTML snippet, title, and a featured image. Currently, I'm going and finding an image for the featured image, but ideally I would be able to automatically download and resize the first frame of the video itself based on the video URL during the saving process of the post itself. If done right, my post type could require nothing more than the link and could get the image and the embed code from that.

For instance, if the video link is http://www.youtube.com/watch?v=2Jnpi-uBiIg the value of v would be extracted and used to download the the image at http://img.youtube.com/vi/2Jnpi-uBiIg/0.jpg.

I'm very new to wordpress development, but something tells me I would be looking into hooks (if I understand those correctly).

share|improve this question
    
I would try to use and extend oEmbed for this, instead of HTML snippet... But having spent some time looking through that code in core - this advice to person new with WP feels like throwing him under bus. :) –  Rarst Nov 27 '10 at 13:38
    
Just really curious if my version worked for you..? –  MikeSchinkel Dec 24 '10 at 13:10
    
@Mike Busy time of the year - I'll need to get back to you :) I do thank you for the information and I will revisit this post shortly to read over all solutions again in the near future. –  Jonathan Sampson Dec 24 '10 at 16:46

4 Answers 4

Hi @Jonathan Sampson:

While this is not exactly what you asked for it might actually be a viable solution and it comes for free from WordPress.com thanks to @yoast's tweet this morning when he referenced this blog post:

Basically you can use WordPress.com' screenshot generator URL in this form (which according to the blog post Matt seemed to bless free usage):

  • http://s.wordpress.com/mshots/v1/{URL-encoded URL}?w={width}

So taking your URL from above:

And then URL encoding so you could use with the screenshot generator you get a URL that looks like this (for a width of 400px):

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

And of course what follows is what the screenshot looks like (I copied the screenshot for posterity so it won't change even if the WordPress service is modified rather than include a direct link to the service to display the image.) Not sure why it snapped the video and not the entire page, but the video is even better for what you want:

Screenshot of a Youtube Video as snapped by WordPress Screenshot Service

Of course the first time you HTTP GET request the URL it will not return a screenshot because WordPress.com's servers need to first capture the screenshot, which takes too much time to let your HTTP GET request wait. So on first request your HTTP GET will just be redirected to this URL:

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

And that URL displays this image:

Generating Preview Image from WordPress.com

But if you wait a minute after your first HTTP GET request and issue the request again you'll find your screenshot. My thoughts are that what you'll want to do is to call it to cache it, wait to download it, and then cache it locally on your own server to minimize the amount of load on WordPress.com's servers so they won't rethink offering this for free (or heck, if there's enough traffic they could even offer it as a paid service and maybe add paid API features too!)

P.S. By the way, to prove that it actually works within a web page here is the screenshot as requested directly from WordPress.com. Note that it may be different from the screenshot I saved and linked above, or it it's been a while since someone viewed this page and thus their cache has clear it might even be the "Generating Preview" image. If so, wait a minute and then refresh this page and it should return:

Screenshot of a Youtube Video directly from WordPress.com Screenshot service

share|improve this answer
    
That looks useful.. :) –  t31os Dec 23 '10 at 14:06

My video blog post generator (http://v.leau.co/) does this but not in wp context.

You supply a query e.g. "superman" (then wait (without notice that it is doing something) (since im the only user)), then click the vids you like to post, click generate code and you have the code where the thumbs are hosted on my site because it downloaded these in the meanwhile. This code can be copy and pasted in a post.

In other words if you would put the code in a function call it would return the piece of code e.g. a href with a link to the video that gets added to the content or e.g. the link to the featured image that is downloaded locally.

Is that the piece of code you are looking for ? I think the core is:

A function to retrieve more results (ergo if you just want to display more than 1 video in the resulting code instead of 1 specific) :

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

A function to get the initial results page:

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

A function to display download the thumbs in a certain directory (variable) and to return a piece of code to put in the post:

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

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

    #
    # there are YouTube urls and also Google Video urls they are both different
    # the one from Google video has the word "ThumbnailServer" in it
    # example:
    # 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";

    #
    # Now that we have the imageFile Name check if we already have it in the cache:
    # - if we have it NEVER delete it (why should we?)
    # - if we dont well... get it via 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 {
            #
            # If our Curl action retrieves nothing then use the default image
            #
            $imgfile="images/default.jpg";
        }
    }

    #
    # Now that we have the image url create a div (hmm.. might drop that
    # styling later on in a seperate csss) containg the video thumb, link
   # and the description. When you like it you can add all the other
    # parameters that Google returns like length 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\"> 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";
    #
    # Note that we also add a delete option, for now that only removes it from the page
    # but in the next version it should do an AJAX call to write the id somewhere so
    # that we never see it again.
    #
    $strBuffer .= $GsearchResultClass["titleNoFormatting"] . "<br />";
    $strBuffer .= "</div></div>\n";
}
return $strBuffer;
}

A function to call the above:

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

(the $key is you Google API key) (http://code.google.com/intl/nl-NL/more/)

IMHO I think the rest is more just "getting the returned stuff and adding it to the content of a post + setting the downloaded cached thumb as featured?

P.S. IMHO it is always better to post a thumbnail in a video when referring to YouTube videos since often older videos are removed by YouTube and they leave ugly postings as a result. With your own thumbs at least the image stays there forever so that afterwards you do have a clue what you posted there originally.

share|improve this answer
    
great answer! What about if you created a custom field for the user to enter the source url for the video and having it execute the image download/local image link upon saving the post? One final step would be that when you update the post with a new URL that the old image is deleted and the new one is added/linked. If you could extend your code with this I think it would be very useful to readers. –  NetConstructor.com Nov 29 '10 at 17:53
    
yeah... but im too busy lol, in 2 weeks i have vacation... –  edelwater Nov 29 '10 at 20:43

I don't know if I understood exactaly what you mean, but I've found this workaround:

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

Best Regards

share|improve this answer
    
@Alvaro Neto - That's actually quite cool, and not something I knew. Too bad they only seem to have tiny thumbnails with that method. –  MikeSchinkel Dec 23 '10 at 12:53

Function to display the image based on the URL? Is this the kind of thing you had in mind?

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\" />";
            break;
        case 'first':
            // Thumbnail of the first frame
            $img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/1.jpg\" />";
            break;
        case 'small':
            // Thumbnail of a later frame(i'm not sure how they determine this)
            $img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/2.jpg\" />";
            break;
        case 'default':
        case '':
        default:
            $img = "<img src=\"http://{$img_server_num}.ytimg.com/vi/{$vid_id[1]}/default.jpg\" />";
            break;
    }
    if( $echo )
        echo $img;
    else
        return $img;

}
// Example calls

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 seems to serve up images for a few servers for the thumbnails..

iN.ytimg.com

Where N is usually a numeric value from 1-4(5 sometimes, but wasn't consistent in testing).

They also use img.youtube.com but i liked the idea of fetching from alternating servers, so i've coded the function to randomly choose from 1 of 4 servers to display the image from.

NOTE: There's not always an image for each of the sizes for every video, some will just come up blank, however the default image seems to consistently work in the video URLs i tested.

Let me know if that helps..

share|improve this answer
    
Nice answer, +1. This was a great question; 4 completely different answers and all seemingly very valid solutions. I just love WA! –  MikeSchinkel Dec 24 '10 at 13:19
    
I looked at the embed class and the filters available, but i couldn't see any easy way to carry the image from the embed object(which does actually contain the image url) to the shortcode functions that output the HTML, the embed object does not currently(as far as i could see) pass along the data needed to the available filters/hooks. Not sure why the embed class doesn't pass along image info(that it has), would make sense to really.. –  t31os Dec 27 '10 at 12:43

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.