Flickr(フリッカー)のアップロード元画像をオリジナルサイズで一括ダウンロードする方法に関するメモです。今回はFlickrにアップロードされた最近の画像をダウンロードしていく手順をサンプルに解説していきたいと思います。

言語はNode.js(ES6)です。また1024px以下の画像アドレスを取得する方法は以下の記事をご覧ください。

目次

まず先に結論ですが、アプリやプラグインなどのプログラムからFlickr APIからオリジナルサイズの画像URLを取得するには、APIを2回叩く必要があります。

具体的には以下の流れになります。

  1. flickr.photos.recentlyUpdatedメソッド等で画像情報を取得(idsecret)
  2. 取得したidsecretを元にflickr.photos.getSizesを実行

この手順で、Flickrにアップロードされているオリジナルサイズ画像のURLを取得することができます。

なぜ2段階でAPIを実行する必要があるの?

FlickrAPIで取得できる写真画像のURLは、下記で示すようにオリジナルサイズ画像のURLだけが異なります。

  • 1024px以内の画像サイズ

    https://farm{farm-id}.staticflickr.com/{server-id}/{photo_id}_{secret}.jpg

    または

    https://farm{farm-id}.staticflickr.com/{server-id}/{photo_id}_{secret}_[mstzb].jpg

  • オリジナル画像サイズ(アップロード時と同じサイズ)

    https://farm{farm-id}.staticflickr.com/{server-id}/{photo_id}_{o-secret}_o.(jpg|gif|png)

例えばFlickrAPIのメソッドflickr.photos.recentlyUpdatedでは次のような情報を一括で取得できます。

api-result.json
1
2
3
4
5
6
7
8
9
10
11
...
{ id: 'xxxxx',
owner: 'xxx@Nxx',
secret: 'xxx',
server: 'xxx',
farm: 1,
title: '写真タイトル',
ispublic: 1,
isfriend: 0,
isfamily: 0 },
...

FlickrAPIを使う時は、上記の情報から画像URLを組み立てます。

また、1024ピクセル以下の各画像サイズにアクセスするには、接尾辞を追記すればアクセスできます。

Flickr API
[mstzb] : サイズ接尾辞 - Size Suffix

しかし、オリジナル画像のURLはo-secretが必要となっており、flickr.photos.recentlyUpdatedメソッドからは取得できません。

o-secretの取得について

残念ながら、オリジナル画像のURLを一括で取得するAPIはFlickrから提供されていません。

そこで、一度写真画像の情報を取得した後で、改めてo-secretを取得するために写真一枚ごとにAPIを叩く必要が出てきます。更にo-secretの取得は限られたFlcikrAPIメソッドからしか取得できないようになっています。

まあ、よく考えたら、

  • farm-id
  • server-id
  • id
  • secret

上記の情報を知ってるだけでオリジナルサイズの画像にアクセスできちゃったら、それはまずいですよね。ブログ公開に使っている小さなflickr画像URLからも、オリジナルURLが推測できちゃう事になります…。

そこでFlickrAPIでは、オリジナル画像を辿れないようにo-secretが用意されていると想像できます。

よくよく考えてみると、このFlickrAPI仕様についてはとても納得がいきますね。

オリジナルサイズの取得には2つのメソッドが用意されてる

冒頭でも触れましたが、Flickrのオリジナルサイズの取得には2種類のメソッドが用意されています。

具体的には、Flickrのオリジナルサイズ画像URLを取得するにはflickr.photos.getInfoまたはflickr.photos.getSizesというメソッドを使うことになります。

オリジナルサイズ画像URLの取得だけが目的であれば、flickr.photos.getSizes(api_key,photo_id)を実行すればOKです。但し非公開の写真にはアクセスできないなどの制約があります。

非公開のオリジナル画像や画像について詳細な情報を取得したい場合はflickr.photos.getInfo(api_key,photo_id,secret)の実行が必要になり、事前にAPIによって取得した写真画像のsecretが必要となります。

Flickr
The App Garden
Flickr API
flickr.photos.getSizes
flickr.photos.getInfo

Flickrのオリジナル画像を取得するコード

フリッカーでオリジナル画像のURLをダウンロードするには、前述した流れでAPIを実行することになります。具体的には以下のようなコードとなります。

get-flickr-original-size.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
const fs        = require("fs")
const path = require("path")
const mkdirp = require('mkdirp')
const FlickrAPI = require("flickrapi")
const Download = require('node-curl-download').Download

// APIの各情報はFlickAPIのThe App Gardenで取得する
const flickrApiOptions = {
api_key : "xxx" ,
secret : "xxx" ,
permissions : "delete" ,
user_id : "xxx@Nxx" ,
access_token : "xxx-xxx" ,
access_token_secret: "xxxx"
}

// 取得を開始したい写真の日時(UNIX Date)と枚数を指定する(以下の例では10枚を取得。1回のAPIで最大500枚の画像情報を取得可能)
const recentlyPhotoOptions = {
min_date : 1388502000 ,
per_page : 10 ,
page : 0
}

// 最近アップロードした画像を取得する
// 長辺1024px以内の画像であれば、以下の関数のみで完了できる
let getRecentlyPhots = () =>
new Promise( ( resolve , reject ) => {
FlickrAPI.authenticate(flickrApiOptions, (error, flickr) => {
if(!error){
flickr.photos.recentlyUpdated(recentlyPhotoOptions, (err, result) => {
if(err) {
console.log("flickr APIの実行に失敗しました")
console.log("エラーメッセージ:")
console.log(err)
console.log("パラメータ:")
console.log(recentlyPhotoOptions)
}else{
if(result && result.photos.total){
for(let p of result.photos.photo){
//console.log(p)
console.log(`bサイズ: https://farm${p.farm}.staticflickr.com/${p.server}/${p.id}_${p.secret}_b.jpg`)
}
resolve(result.photos.photo)
}else{
console.log("取得できる写真データがありませんでした")
reject()
}
}
})
}else{
console.log("flickr APIのアクセスに失敗しました")
reject()
}
})
})

// 画像のオリジナルサイズを取得する
// このAPIを実行するには前提条件がある。事前に実行したAPIにより、Flickr写真のidとsecretが分かっている必要がある。
let getOriginalSize = (photos) =>
photos.reduce( (promise, value) =>
promise.then( (editedArray) =>
parallel_getOrgSize(value).then( (editedElement) => {
editedArray.push(editedElement)
return editedArray
})
)
, Promise.resolve([]))

let parallel_getOrgSize = (p) =>
new Promise( ( resolve , reject ) => {
FlickrAPI.authenticate(flickrApiOptions, (error, flickr) => {
let parameter = {
"api_key" : flickrApiOptions.api_key ,
"photo_id" : p.id ,
"secret" : p.secret
}

if(!error){
flickr.photos.getInfo(parameter, (err, result) => {
if(err) {
console.log("flickr APIの実行に失敗しました")
console.log("エラーメッセージ:")
console.log(err)
console.log("パラメータ:")
console.log(parameter)
reject()
}else{
console.log(`\u001b[32moサイズ:\u001b[0m https://farm${p.farm}.staticflickr.com/${p.server}/${p.id}_${result.photo.originalsecret}_o.jpg`)
resolve(`https://farm${p.farm}.staticflickr.com/${p.server}/${p.id}_${result.photo.originalsecret}_o.jpg`)
}
})
}else{
console.log("flickr APIのアクセスに失敗しました")
reject()
}
})
})



// 指定URLをダウンロードする処理
let downloadFile = (photos) =>
photos.reduce( (promise, value) =>
promise.then( (editedArray) =>
parallel_dl(value).then( (editedElement) => {
editedArray.push(editedElement)
return editedArray
})
)
, Promise.resolve([]))

let parallel_dl = (url) =>
new Promise( ( resolve , reject ) => {
let dist = path.join( 'downloaded' , path.basename(url) )
mkdirp( 'downloaded' , ( err ) => {
if(!err){
let dl = new Download(url, dist )
dl.on('progress', (progress) => {
// console.log("" + progress + "%")
})

dl.on('end', (code) => {
if (code === 0) {
console.log(`\u001b[36mダウンロード完了: \u001b[0m${url}`)
resolve()
} else {
console.log(`ファイルのダウンロードに失敗しました。url:${url}`)
reject()
}
})

dl.start()
}else{
console.log("ディレクトリの作成に失敗しました。")
}
})
})


// 各関数の実行
getRecentlyPhots()
.then(getOriginalSize)
.then(downloadFile)
.then( (inObj) => {
return new Promise( (resolve , reject) => {
process.exit(1)
resolve()
})
})

上記はNode.jsで動作するコードです。

node.jsやnpmそしてコマンドラインについてご不明な場合は以下をご覧ください。

ということで

今回はフリッカーにアップロードされた元画像を、元の大きさでまとめてダウンロードする方法について触れました。

オリジナルサイズ画像を取得するのは、簡単そうに見えて、実はぜんぜん簡単じゃなかったです(^^;)これに気づかずflickrプラグインを作成している方も結構いらっしゃるんじゃないかと思いました。bサイズ(長辺1024px)が取得できるなら、oサイズ(オリジナルサイズ)も取得できるよね?みたいな。

でも実際はAPIを2回叩かないと取得できない仕様設計になっています。

このブログで使ってるhexo-tag-flickrもまさにそんな感じでした。作者さんが多分気づいていないですね…。

ただ、2回APIを叩くということはプラグインの実行時間もかなり増える事になります。ここは割りきってbサイズまでしか取得できない仕様と割り切るか、oサイズも取得できるけど、キャッシュしてそれ以降は実行時間を短縮できるように工夫するかは作者次第ということになりそうです。