Automating a Daily Picture Blog?

I was wondering if anyone could give me some suggestions on how best I could streamline the following;

I have a WordPress blog (privately hosted) which I update daily (well, I normally schedule a load of updates at once) with pictures which I upload, tag and write a very brief comment on.

Read More

I was originally considering a folder on the server I could FTP images into and then I would write something that would look there daily and create a post based on the picture, but then I wasn’t sure about the meta data that I want along with it.

Any help would be greatly appreciated.

P.S I would make this a community wiki (as I am not sure there is even one answer let alone one stand out correct answer!) but I don’t have enough the right privileges yet!

Related posts

Leave a Reply

2 comments

  1. There are lots of ways to accomplish a daily picture blog so I’ll just give you how I’d approach it.

    If I wanted to set up a photoblog on WordPress I’d start with a Flickr account and leverage it (or if you don’t like Flickr for some reason you can also look at PhotoBucket, SmugMug, Picasa or one of the other Flickr alternatives.)

    Using Flickr

    With Flickr there are bulk uploading tools to make it easy for your to upload lots of photos including lots of tools for managing a large number of photos.

    Flickr itself has built-in tools to support photo-blogging including email-to-blog.

    And if the existing tools don’t give you enough control with Flickr you can always write some PHP scripts to integrate and automate. There are lots of blog posts to show you how (and I’ll show you how to write one as well, below):

    And at least a few PHP libraries to simplify access to the Flickr API:

    Exploring the Flickr API

    So let’s say you want to do a little simple photo blogging to WordPress using files that have been uploaded to Flickr? The first step is to get an API Key:

    While you are at it you might want to just scan the API docs:

    Now there are a bunch of different ways to query Flickr via their API. I’ll show how by Querying a Photo Set but you could also consider Searching by Tags.

    For the first example I’ll use my Flickr photo set entitled Barber Motorcycle Museum – Oct 21-22 2006” (I’m using it because people seem to like it; it gets lots of traffic.) You’ll need to get the photoset_id which you can find in the URL:

    Where to find 'photoset_id' on a Flickr Photo Setlink text
    (source: mikeschinkel.com)

    Next you need to call the flickr.photosets.getPhotos method of the Flickr API with a URL of the following format:

    http://api.flickr.com/services/rest/?method=flickr.photosets.getPhotos&api_key={YOUR_API_KEY_GOES_HERE}&photoset_id=72157594340403773&media=photos&format=json&nojsoncallback=1
    

    Note that once you replace {YOUR_API_KEY_GOES_HERE} with your own API key you can actually call it from your browser and it will look something like this:

    Calling the Flickr API directly from your Browser
    (source: mikeschinkel.com)

    Also note that I request JSON back using the format=json&nojsoncallback=1 URL parameters because I find it much easier to handle JSON than XML, and the nojsoncallback omits the callback on would need if working in Javascript.

    What you’ll get back is a nested data structure containing a list of photos, and for each photos you’ll get an object with the following attributes (what I’m showing below it the output of the first element of the array of photos as generated by the standard PHP print_r() function):

    [0] => stdClass Object
      (
        [id] => 276672853
        [secret] => 099eaa1af2
        [server] => 107
        [farm] => 1
        [title] => Barber Motorcycle Museum - Oct 22 2006 (500)
        [isprimary] => 0
      )
    

    Flickr’s Photo URL Formats

    Now that we have our list of recent photos you next need to understand Flickr’s Photo URL format which you can read more about here:

    In general Flickr’s photo URLs go like this:

    500px Wide or High “Standard” Photo:

    • http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg

    This version will be 500 pixels on its longest side.

    The Original Image:

    • http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{o-secret}_o.(jpg|gif|png)

    This can be whatever was uploaded, a .JPG, a .GIF or a .PNG.

    Specific Sized Versions:

    These are always .JPG:

    • http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[size].jpg

    Where [size] is one of:

    • s – small square 75×75
    • t – thumbnail, 100 on its longest side
    • m – small, 240 on its longest side
    • z – medium, 640 on its longest side
    • b – large, 1024 on its longest side (only exists for very large original images)

    So the following URL taken from the example results returned above pulls the photo that follows (note how the z before the .jpg indicates a photo 640 pixels wide on the longest size:)

    Photo of Motorcycle on Flickr

    Armed with all of the above information we can now create a PHP script using WordPress’ WP_Http class to retrieve the photos from our set. I wrote a simple script which you can copy into the root of your website being sure to replace with your own api_key and photoset_id before running it:

    include "wp-load.php";
    define('FLICKR_API_KEY','{YOUR_API_KEY_GOES_HERE}');
    define('FLICKR_GET_URL','http://api.flickr.com/services/rest/');
    define('PHOTO_SIZE_CHAR', 's'); // s = 75x75 pixels on longest side
    $result = flickr_get(array(
      'method'      => 'flickr.photosets.getPhotos',
      'photoset_id' => '{YOUR_PHOTOSET_ID_GOES_HERE}',
      'media'       => 'photos'
    ));
    if (isset($result['body']->photoset->photo)) {
      $size = PHOTO_SIZE_CHAR;
      foreach($result['body']->photoset->photo as $photo) {
        extract((array)$photo);
        $url = "http://farm{$farm}.static.flickr.com/{$server}/{$id}_{$secret}_{$size}.jpg";
        echo "<img src="{$url}" alt="{$title}" />n";
      }
    }
    function flickr_get($params,$args=array()) {
      $http = new WP_Http();
      $params['api_key'] = FLICKR_API_KEY;
      $params['format'] = 'json';
      $params['nojsoncallback'] = 1;
      $params = http_build_query($params);
      $url = FLICKR_GET_URL . "?{$params}";
      $result = $http->get($url,$args);
      if (isset($result['response']['code']) && $result['response']['code'] == 200)
        $result['body'] = json_decode($result['body']);
      else
        $result = false;
      return $result;
    }
    

    Using the above with my API key and my Photoset ID I get the following:

    Output of a PHP Script Showing a Flickr Photoset
    (source: mikeschinkel.com)

    PHP Script for Posting of a Flickr Photoset Photo to your Blog

    Based on all that I’ve written a page called blog-from-flickr.php that you can put in the root of your website and call to get the following simple form:

    Blog From Flickr PHP Script for WordPress
    (source: mikeschinkel.com)

    Enter a valid Photoset ID and then click that button and you’ll get be redirected to a blog post that looks something like this!

    Blog Post Generated from a Flickr Photoset Photo via a PHP Script
    (source: mikeschinkel.com)

    To get it all working was a bit more involved than the simple code above and more than I have time left to fully document but I’ll post it below for your review (and you can also download it from here).

    Of special note are the following aspects in the code:

    • The script pulls the twenty five (25) most recent photos from the photoset and blog the earliest one of those. I assumed you’d want to upload in bulk and then post chronologically. If you upload more than 25 then you need to change the value of the constant FLICKR_PHOTO_COUNT to something larger, up to 500 (500 is the limit specified by Flickr.) Increasing it will only slightly increase how long it takes the script to run; it might not even be noticeable.

    • The script stores Flickr’s photo ID as a hidden custom field in wp_postmeta using the key '_flickr_photo_id'. That’s how I can tell if it’s been blogged before or not.

    • The script uses an HTTP POST method (i.e. using an HTML <form>) vs an HTTP GET method (calling a URL directly) to avoid accidentally triggering the posting process and to allow posting from a different Photoset.

    • The script uses current_user_can('publish_posts') to ensure only a logged-in user with posting rights can trigger the posting logic.

    • The script grabs the photo’s description from Flickr and use it as a prefix to the <img> tag for the post content (note my example screenshot didn’t have a description hence you see only the photo.)

    • The script calls PHP’s getimagesize() with a URL of the photo to get the height and width for the <img> tag; if your server is not configured for that you won’t get size and width tags on your images (actually the code might crash; mine works so I couldn’t test. If it crashes, you’ll know. 🙂

    • I didn’t go to the effort to bring the tags over from Flickr and add them as tags, but that would be a worthwhile exercise for someone else.

    And that’s about it. On to the code…

    The Flickr Photo Blogging Code:

    <?php
    /*
    
      blog-from-flickr.php
    
      Allows the WordPress blog owner to blog a recent photo from a Flickr Photoset just by clicking a button.
    
      Author: Mike Schinkel (http://mikeschinkel.com)
    
      Just drop this example into the root of your website and call directly to see it work.
      Use the class in your plugins or themes.
    
      In Answer To: http://wordpress.stackexchange.com/questions/2830/
    
    */
    
    include "wp-load.php";
    define('FLICKR_API_KEY','{YOUR_FLICKR_API_KEY_GOES_HERE}');
    define('DEFAULT_PHOTOSET_ID','{YOUR DEFAULT_PHOTOSET_GOES_HERE');
    define('FLICKR_PHOTO_COUNT',25);
    
    define('FLICKR_GET_URL','http://api.flickr.com/services/rest/');
    
    $photoset_id = DEFAULT_PHOTOSET_ID;
    
    if (count($_POST)==0)
      echo <<<HTML
    <html>
    <body>
      <form method="post">
        Enter Flickr Photoset ID: <input type="text" name="photoset_id" value="{$photoset_id}" size="25" />
        &nbsp;<input type="submit" value="Blog Recent Photo from Flickr!" />
        <input type="hidden" name="go" value="go!" />
      </form>
    </body>
    </html>
    HTML;
    else
      BlogFlickrPhotoSetPhoto::blog_photoset_photo($_POST['photoset_id']);
    
    class BlogFlickrPhotoSetPhoto {
      static function blog_photoset_photo($photoset_id) {
        if (current_user_can('publish_posts')) {
          $photoset = self::get_photoset_photos($photoset_id);
          if (!$photoset) {
            echo 'Not a valid Photoset ID. <a href="#">Try again</a>.';
          } else {
            $photo = self::get_photo_to_blog($photoset);
            if (!$photo) {
              echo 'Unexpected Error. Photo Retrieval Failed.';
            } else {
              $permalink = self::blog_photo($photo);
              wp_safe_redirect($permalink);
            }
          }
        }
      }
      static function blog_photo($photo) {
        $img = self::get_photo_html($photo);
        $photo_info = self::get_photo_info($photo->id);
        $description = ($photo_info ? "{$photo_info['body']->photo->description->_content}<br/>" : '');
        $url = self::get_photo_url($photo);
        $post_id = wp_insert_post(array(
          'post_title' => $photo->title,
          'post_type' => 'post',
          'post_author' => 1,
          'post_content' => "{$description}{$img}",
          'post_status' => 'publish',
          'comment_status' => 'open',
          'ping_status' => 'open',
          'post_parent' => 0,
        ));
        if ($post_id) {
          update_post_meta($post_id,'_flickr_photo_id',$photo->id);
          $url = get_permalink($post_id);
        } else {
        $url = false;
        }
        return $url;
      }
      static function get_photo_to_blog($photoset) {
        global $wpdb;
        $photo = false;
        if (isset($photoset['body']->photoset->photo)) {
    
          //Collect the list of Flickr Photo_ids from our "N" most resent photoset photos
          $photos = $photoset['body']->photoset->photo;
          foreach($photos as $index => $photo) {
            $photo_ids[$index] = $photo->id;
          }
          $photo_id_list = "'" . implode("','",$photo_ids) . "'";
    
          //Get a list of all photos we've already blogged
          $posts = $wpdb->get_results("SELECT post_id,meta_value FROM wp_postmeta WHERE meta_key='_flickr_photo_id' && meta_value IN ({$photo_id_list})");
    
          //Remove any photos from the list we've already blogged
          $photo_ids = array_flip($photo_ids);
          foreach($posts as $post) {
            if (isset($photo_ids[$post->meta_value])) {
              unset($photo_ids[$post->meta_value]);
            }
          }
    
          //Grab the earliest photo we've not blogged
          if (count($photo_ids)==0) {
            $photo = false;
          } else {
            krsort($photo_ids);
            $photo = $photos[reset($photo_ids)];
          }
        }
        return $photo;
      }
      static function get_photoset_photos($photoset_id) {
        $photoset = self::flickr_get(array(
          'method'      => 'flickr.photosets.getPhotos',
          'photoset_id' => $photoset_id,
          'media'       => 'photos',
          'per_page'    => FLICKR_PHOTO_COUNT,
        ));
        return $photoset;
      }
      static function get_photo_info($photo_id) {
        $photo_info = self::flickr_get(array(
          'method'   => 'flickr.photos.getInfo',
          'photo_id' => $photo_id,
        ));
        return $photo_info;
      }
      static function get_photo_html($photo,$size='') {
        $url = self::get_photo_url($photo,$size);
        list($width, $height) = getimagesize($url);
        $hwstring = image_hwstring($width, $height);
        return "<img src="{$url}" alt="{$title}" {$hwstring} />n";
      }
      static function get_photo_url($photo,$size='') {
        extract((array)$photo);
        $size = (!empty($size) ? "_{$size}" : ''); // If empty the 500px version will be returned.
        return "http://farm{$farm}.static.flickr.com/{$server}/{$id}_{$secret}{$size}.jpg";
      }
      static function flickr_get($params,$args=array()) {
        $http = new WP_Http();
        $params['api_key'] = FLICKR_API_KEY;
        $params['format'] = 'json';
        $params['nojsoncallback'] = 1;
        $params = http_build_query($params);
        $url = FLICKR_GET_URL . "?{$params}";
        $result = $http->get($url,$args);
        if (isset($result['response']['code']) && $result['response']['code'] == 200)
          $result['body'] = json_decode($result['body']);
        else
          $result = false;
        return $result;
      }
    }
    

    Fully Automatic Flickr Photo Blogging

    If you need to go even farther, you can recode the above to use a pseudo-cron instead of triggering on an HTTP POST. Here’s a link to a few articles that can help with that:

    And if pseudo-cron isn’t good enough, you can alway rename the script to an obscure name and use real cron to retrieve that obscurely-named URL. If you need to go that route, check Google to learn how to set up a cron task..

    In either case with cron you’ll probably have to remove the current_user_can() code because you won’t be logged in.

    P.S. Getting your Flickr NSID (User ID)

    Beyond the above you may find you need to get your Flickr NSID (aka your user ID) for some of the methods, you can find it here:

    P.P.S. Authenticating a PHP Script with Flickr is Harder

    You may note that none of my examples used required the caller to be authenticated. It’s a good bit more involved to authenticate so if you need that be sure to look into the Flickr PHP libraries mentioned as I think they handle that.

  2. I would actually recommend using the built-in post by email functionality instead. You set up a private email address (mysupersecretaddress12345@mydomain.com) and send both the images and your descriptions to that address. WordPress will then create a new post from your email and automatically attach the image.

    This is a bit easier than FTP.