Auto-retrieve YouTube Image for Thumbnail?

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.

Read More

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

Related posts

Leave a Reply

4 comments

  1. 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):

    • https://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):

    https://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
    (source: mikeschinkel.com)

    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:

    • https://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

  2. 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.

  3. 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..