Flickrのアップロード元画像をオリジナルサイズで一括ダウンロードする方法に関するメモです。
Golden apple | Sony α7 + Sonnar T* FE 55mm F1.8 ZA photo by tea © まず先に結論ですが、アプリやプラグインなどのプログラムからFlickr APIからオリジナルサイズの画像URLを取得するには、APIを2回叩く必要があります。
具体的には以下の流れになります。
flickr.photos.recentlyUpdated
メソッド等で画像情報を取得(id
とsecret
) - 取得した
id
とsecret
を元に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.json1 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.js1 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
const flickrApiOptions = { api_key : "xxx" , secret : "xxx" , permissions : "delete" , user_id : "xxx@Nxx" , access_token : "xxx-xxx" , access_token_secret: "xxxx" }
const recentlyPhotoOptions = { min_date : 1388502000 , per_page : 10 , page : 0 }
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(`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() } }) })
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() } }) })
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) => { })
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そしてコマンドラインについてご不明な場合は以下をご覧ください。
node.jsやコマンドラインについて
node.jsの環境を用意する方法やコマンドラインの使い方については以下を併せてご覧ください。
ということで
今回はフリッカーにアップロードされた元画像を、元の大きさでまとめてダウンロードする方法について触れました。
オリジナルサイズ画像を取得するのは、簡単そうに見えて、実はぜんぜん簡単じゃなかったです(^^;)これに気づかずflickrプラグインを作成している方も結構いらっしゃるんじゃないかと思いました。bサイズ(長辺1024px)が取得できるなら、oサイズ(オリジナルサイズ)も取得できるよね?みたいな。
でも実際はAPIを2回叩かないと取得できない仕様設計になっています。
このブログで使ってるhexo-tag-flickrもまさにそんな感じでした。作者さんが多分気づいていないですね…。
ただ、2回APIを叩くということはプラグインの実行時間もかなり増える事になります。ここは割りきってbサイズまでしか取得できない仕様と割り切るか、oサイズも取得できるけど、キャッシュしてそれ以降は実行時間を短縮できるように工夫するかは作者次第ということになりそうです。