How to make a gallery for Flickr photos

How to make a gallery for Flickr photos

This post is for those who want to showcase photos stored by Flickr. There is a commercial product for this purpose (see Flickr Pack). But you can easily do the same (say, almost) absolutely for free.

I have already covered the theme of usage of Moon Gallery plugin in two little posts (see here and here). Now it’s time to go further and to tell about its usage with Flickr API.

You can  read all about Flickr API here.

The general  scheme of using this API is as follows:

  1. The script sends an appropriate query to the server, using one of the methods. All the information must be encoded in the URL string.
  2. The server responds to the query and sends back the result using one of the supported formats (we need JSON).

That’s all (in general).

The good thing is that our Moon Gallery plugin works the same way: it sends an URL and waits back JSON.

The bad thing is that the JSON, that it gets from Flickr, is not in the appropriate format.

Fortunately the Moon Gallery plugin has a parameter jsonParser. This is a user-defined function that takes a received JSON object as a parameter and returns a JSON object in appropriate format.

OK. Let’s start.

Methods

We can choose from one of the following Flickr API methods:

  • flickr.photos.getRecent – for recent photos
  • flickr.photos.search – for search queries
  • flickr.people.getPublicPhotos – for photos of a user
  • flickr.galleries.getPhotos – for photos from a gallery
  • flickr.photosets.getPhotos – for photos from a photoset

Today I’m going to choose photos of a user. Let’s create a gallery!

Query

First of all we need to send a request to the server. So we should build an URL string.

We need to specify parameters:


var api_key = API_KEY, //you should get your own one
    method = 'flickr.people.getPublicPhotos',
    user_id = '63501627@N06', //use it if you are looking for photos of a user
    per_page = 100, //photos per page
    extras = 'owner_name, media, url_n, url_m, url_c, url_z, url_l, date_taken';


var params = {
    method: method,
    api_key: api_key,
    user_id: user_id,
    format: 'json',
    page: 1,
    per_page: per_page,
    extras: extras
 };

And we need a function that will create an url from the parameters object:


var toUrl = function(o) {
      return  _.reduce(_.keys(o), function(memo, key) {
         if (o[key] == null) return memo;
         return memo + encodeURIComponent(key) + '=' + encodeURIComponent(o[key])+'&';
       }, '');
     };

As you can see, I use Underscore library here. Why? Because our Moon Gallery plugin itself uses Underscore.js.
Using this function I can form the string:


var urlBasic = 'https://api.flickr.com/services/rest/?';
var urlEnd = 'jsoncallback=?';


var url = urlBasic + toUrl(params) + urlEnd;

Parser

Now let’s define our jsonParser function.

The full code of this function:


var jsonParser = function(data) {
  if(!data) throw new Error('no data');
  if(data.stat === 'fail') {
    if(data.message && _.isString(data.message)) throw new Error(data.message);
    else throw new Error('Unknown Error');
  }
  if(!data.photos) throw new Error('the json object may have error');
  
  // the array of data:
  var photos = _.map(data.photos.photo, function(item) {

    var PREFFERED_HEIGHT = 350;  // you can change the size here

    var id = item.id,
        farm = item.farm,
        owner = item.owner,
        secret = item.secret,
        server = item.server,
        title = item.title,
        date = item.datetaken.split(' ')[0],
        owner_name = item.ownername,
        media = item.media,
        
        hrefToPage = 'http://flickr.com/photos/' + owner + '/' + id + '/in/photostream',
        
        url_n = item.url_n,  // 320px
        url_m = item.url_m,  // 500px
        url_c = item.url_c,  // 640px
        url_z = item.url_z,  // 800px
        url_l = item.url_l,  // 1024px

        width_n = item.width_n *1,
        width_m = item.width_m *1,
        width_c = item.width_c *1,
        width_z = item.width_z *1,
        width_l = item.width_l *1,

        height_n = item.height_n *1,
        height_m = item.height_m *1,
        height_c = item.height_c *1,
        height_z = item.height_z *1,
        height_l = item.height_l *1;

        var heights = [
          ['url_n', height_n],
          ['url_m', height_m],
          ['url_z', height_z],
          ['url_c', height_c],
          ['url_l', height_l]
        ];

        var urls = {
          url_n: url_n,
          url_m: url_m,
          url_z: url_z,
          url_c: url_c,
          url_l: url_l
        };

    // all items must by of photo type
    if (media === 'video') return null;

    // looking for an icon url:
    var icon = urls[
      _.chain(heights)

      .filter(function(item){
        if (_.isNaN(item[1])) return false;
        return item[1] > PREFFERED_HEIGHT;
      })

      .min(function(item){
        return item[1];
      })

      .value()[0]
    ];

    // looking for a big photo url:
    var showImage = (function(urls){
      if(urls.url_l && _.isString(urls.url_l)) return urls.url_l;
      if(urls.url_c && _.isString(urls.url_c)) return urls.url_c;
      if(urls.url_z && _.isString(urls.url_z)) return urls.url_z;
      if(urls.url_m && _.isString(urls.url_m)) return urls.url_m;
      if(urls.url_n && _.isString(urls.url_n)) return urls.url_n;

    })(urls);

    if(!icon) return false;

    return {
      title: title, //title
      topDescription: owner_name, //description
      src: icon, //a src of an icon
      href: showImage, //a href to a big image
      lb: { //lightbox
        title: title, //a title
        href: hrefToPage //the link to an external page
      }
    };
  });


  //metadata:
  var meta = {
    page: data.photos.page, //a page of pages
    pages: data.photos.pages, //number of pages
    perpage: data.photos.perpage, // how many photos are in one page
    total: data.photos.total //total number of photos
  };

  photos = _.filter(photos, function(item) {return item});

  return [photos, meta];
};

Yes. It’s a rather big function! I’ll try to explain it.
The incoming JSON that we need to be parsed looks like that:


{
    "photos": {
        "page": 1,
        "pages": 82,
        "perpage": 5,
        "total": "408",
        "photo": [{
            "id": "35027370196",
            "owner": "63501627@N06",
            "secret": "34731b47e7",
            "server": "4288",
            "farm": 5,
            "title": "Ground level ",
            "ispublic ":1,
            "isfriend ":0,
            "isfamily ":0,
            "datetaken ":"2017 - 06 - 03 10: 27: 36 ",
            "datetakengranularity ": "0",
            "datetakenunknown": "0",
            "ownername": "vabserk",
            "media": "photo",
            "media_status": "ready",
            "url_n": "https: \/\/farm5.staticflickr.com\/4288\/35027370196_34731b47e7_n.jpg",
            "height_n":196,
            "width_n":"320",
            "url_m": "https:\/\/farm5.staticflickr.com\/4288\/35027370196_34731b47e7.jpg",
            "height_m": "307",
            "width_m": "500",
            "url_c": "https:\/\/farm5.staticflickr.com\/4288\/35027370196_34731b47e7_c.jpg",
            "height_c": 491,
            "width_c": "800",
            "url_z": "https:\/\/farm5.staticflickr.com\/4288\/35027370196_34731b47e7_z.jpg",
            "height_z": "393",
            "width_z": "640",
            "url_l": "https:\/\/farm5.staticflickr.com\/4288\/35027370196_34731b47e7_b.jpg",
            "height_l": "628",
            "width_l": "1024"
        }
        ...
        
        ]},
    "stat ":"ok "
}

There are some fields with metadata, but the major field is “photo”. It’s an array of objects, each of them describes one photo of the flickr photostream.

Our function returns a tuple of two elements. First element is an array of objects, each of them defines one photo in appropriate format. Second one is an object of metadata:


[
  [
    {
      "title": "Ground level",
      "topDescription": "vabserk",
      "src": "https://farm5.staticflickr.com/4288/35027370196_34731b47e7_z.jpg",
      "href": "https://farm5.staticflickr.com/4288/35027370196_34731b47e7_b.jpg",
      "lb": {
        "title": "Ground level",
        "href": "http://flickr.com/photos/63501627@N06/35027370196/in/photostream"
      }
    }
    
   ...

  ],
  {
    "page": 1,
    "pages": 82,
    "perpage": 5,
    "total": "408"
  }
]

We use the map function to traverse through the collection of photos. The main problem that must be solved inside this block is to fined the most suitable url of the photo. The matter is that Flickr stores several files with different sizes for one photo. So there are several fields for that purpose: url_n, url_m, url_c, url_z, url_l.
But depends on the size of the original photo some fields may be undefined.

Let’s define some variables for every photo:


var PREFFERED_HEIGHT = 350,

...

    url_n = item.url_n, // 320px
    url_m = item.url_m, // 500px
    url_c = item.url_c, // 640px
    url_z = item.url_z, // 800px
    url_l = item.url_l, // 1024px

    width_n = item.width_n * 1,
    width_m = item.width_m * 1,
    width_c = item.width_c * 1,
    width_z = item.width_z * 1,
    width_l = item.width_l * 1,

    height_n = item.height_n * 1,
    height_m = item.height_m * 1,
    height_c = item.height_c * 1,
    height_z = item.height_z * 1,
    height_l = item.height_l * 1;

We shall compare different heights of the photo files with our PREFFERED_HEIGHT value. If a photo doesn’t have a file with height bigger than PREFFERED_HEIGHT, the photo will be ignored. This code finds the url of an icon:


var heights = [
    ['url_n', height_n],
    ['url_m', height_m],
    ['url_z', height_z],
    ['url_c', height_c],
    ['url_l', height_l]
];

var urls = {
    url_n: url_n,
    url_m: url_m,
    url_z: url_z,
    url_c: url_c,
    url_l: url_l
};

// looking for an icon url:
var icon = urls[
    _.chain(heights)

    .filter(function(item) {
        if (_.isNaN(item[1])) return false;
        return item[1] > PREFFERED_HEIGHT;
    })

    .min(function(item) {
        return item[1];
    })

    .value()[0]
];

And this function finds an url for the lightbox (biggest one):


var showImage = (function(urls){
      if(urls.url_l && _.isString(urls.url_l)) return urls.url_l;
      if(urls.url_c && _.isString(urls.url_c)) return urls.url_c;
      if(urls.url_z && _.isString(urls.url_z)) return urls.url_z;
      if(urls.url_m && _.isString(urls.url_m)) return urls.url_m;
      if(urls.url_n && _.isString(urls.url_n)) return urls.url_n;

    })(urls);

So as we have an url for our request and we defined a parser function we can create our grid at last:


var options = {
    retina: 0,

    timeout: 10000,
    grid: '#container',
    url: url,
    jsonParser: jsonParser,
    lightbox: {
       retina: false,
       swipe: true
    }
};

MMG.Gallery('Classica', options);

That’s all, in general. As you can see you can set Moon Gallery Plugin for your personal needs easily.

You can see this example here.

Of course, if you don’t want to bother with all this code, you can choose a commercial Flickr Pack.

About Alexandre Kremlianski

Scala / Scala.js / JavaScript programmer

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.