2019年11月5日火曜日

Google Photos API を使ってみた

概要

Google Photos API を使って Google Photo アプリを作成してみました
OAuth2 認証を使って認証したユーザが持つ写真とアルバムの情報を表示してみます

環境

  • macOS 10.15
  • Ruby 2.6.2p47
  • Google Photos API (2019/11/05 時点)

Google Photos API の有効化

初めての場合はここから API を有効にできます

「Enable the Google Photos Library API」を選択します

プロジェクトを選択するダイアログが表示されるので API を有効にするプロジェクトを選択します

OAuth をコールするクライアントを選択します
今回はブラウザアプリを作成するので「Web browser」を選択します
CORS は全部許可するのでとりあえず入力しないで OK です

API が有効になるのを待ちます

有効になったら API をコールするためのクレデンシャルファイルがダウンロードできます
が、情報が不十分なクレデンシャルなのでダウンロードは一旦保留しておきます

OAuth クライアントの確認/クレデンシャルの取得

API とサービスの画面から「認証情報」を選択します
「OAuth client」というクライアントが作成されているのを確認しましょう

リダイレクト URL が設定されていない場合は設定しましょう
今回は http://localhost:9292/redirect を設定しました
また今回は設定していませんが「承認済みの JavaScript 生成元」に http://localhost:9292 を設定しても OK です
設定したら保存して上にある「JSON をダウンロード」を選択してクレデンシャルの JSON ファイルを保存します
またダウンロードしたクレデンシャルファイルの名前を「client_secrets.json」に変更しましょう

左メニューにある「OAuth同意画面」も設定しましょう

ライブラリインストール

ここからはコーディングです
まずは必要なライブラリをインストールします

  • bundle init
  • vim Gemfile
gem "sinatra"
gem "google-api-client"
  • bundle install --path vendor

サンプルアプリ

ダウンロードした client_secrets.json はアプリのルートディレクトリに配置します

  • vim config.ru
require './app'
run TestWebApp
  • vim app.rb
require 'sinatra/base'
require 'google/api_client/client_secrets'
require 'json'
require 'net/https'

class TestWebApp < Sinatra::Base
  enable :sessions

  before do
    client_secrets = Google::APIClient::ClientSecrets.load
    @auth_client = client_secrets.to_authorization
  end

  get '/' do
    @auth_client.update!(
      :redirect_uri => 'http://localhost:9292/redirect',
      :scope => [
        'https://www.googleapis.com/auth/photoslibrary.readonly',
        'https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata'
      ]
    )
    auth_uri = @auth_client.authorization_uri.to_s
    redirect auth_uri
  end

  get '/redirect' do
    @auth_client.code = request['code']
    @auth_client.fetch_access_token!
    auth_client = Signet::OAuth2::Client.new(JSON.parse(@auth_client.to_json))
    session[:token] = auth_client.access_token
    redirect '/list'
  end

  get '/list' do
    uri = URI.parse 'https://photoslibrary.googleapis.com/v1/albums'
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    req = Net::HTTP::Get.new uri.request_uri
    req['Content-type'] = 'application/json'
    req['Authorization'] = "Bearer #{session[:token]}"
    res = http.request req
    # "<pre>#{JSON.pretty_generate(JSON.parse(res.body))}</pre>"
    @albums = JSON.parse(res.body)
    erb :list
  end

  get '/:album_id/list' do
    uri = URI.parse 'https://photoslibrary.googleapis.com/v1/mediaItems:search'
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    req = Net::HTTP::Post.new uri.request_uri
    req['Content-type'] = 'application/json'
    req['Authorization'] = "Bearer #{session[:token]}"
    req.body = { 
      "pageSize" => "100",
      "albumId" => params['album_id']
    }.to_json
    res = http.request req
    @media_items = JSON.parse(res.body)
    erb :items
  end

end
  • mkdir views/
  • vim views/list.erb
<html>
  <head>
    <title>test web app</title>
  </head>
  <body>
    <h1>Albums</h1>
    <ul>
      <% @albums['albums'].each do |album| %>
        <li><a href="/<%= album['id'] %>/list"><%= album['title'] %></a></li>
      <% end %>
    </ul>
  </body>
</html>
  • vim views/items.erb
<html>
  <head>
    <title>test web app</title>
  </head>
  <body>
    <h1>MediaItems</h1>
    <ul>
      <% @media_items['mediaItems'].each do |item| %>
        <li><a href="<%= item['productUrl'] %>" target="_blank"><%= item['filename'] %></a></li>
      <% end %>
    </ul>
  </body>
</html>

説明

まずは OAuth2 認証します
/ にアクセスすると認証が開始されます
認証部分は SDK の機能が使えるのでそのまま使います

認証自体は Google が用意してくれている同意画面を使います
許可するスコープは「https://www.googleapis.com/auth/photoslibrary.readonly」と「https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata」になります
前者はアルバムへのアクセス権限で後者はアルバムの配下の写真にアクセスするための権限です
Google 側で認証と認可が成功するとアプリの /redirect に戻ってきます
認証が成功していると Google Photos API をコールするためのセッショントークンが返ってきます

このセッショントークンを使って Google Photos API をコールします
Ruby の google-api-client には Google Photos API の SDK が含まれていないので直接 REST API をコールしています
今回はアルバムの一覧を表示する画面とアルバム配下の写真の一覧を表示するビューを作成しています
写真は 100 枚ごとに取得しておりもし次のページが存在する場合は nextPageToken が付与されています
これを使ってリクエストすることで次のページの写真を取得することができます (参考)

動作確認

  • bundle exec rackup config.ru

http://localhost:9292 にアクセスしましょう
認証画面が出るのでユーザを選択します

警告画面が表示されますがテストなので気にせず移動します
もし「承認済みの JavaScript 生成元」を設定している場合はこの警告はでません

アプリが使用する権限が表示されるので「許可」します

内容を確認して許可します

最初にアルバムの一覧が表示されます

アルバムを選択するとアルバム配下の写真の一覧が表示されます
写真のリンクをクリックすると実際の写真を確認することができます

最後に

Google Photos API を Ruby からコールしてみました
Ruby には専用の SDK がなかったですが Java と Node.js には実装されているようです
API をコールする前に OAuth2 の認証を設定/実装する必要があるのでそっちが大変かなと思います

また今回はアルバムと写真の取得の API のみ使っています
他にも共有アルバムを取得する API や写真をアップロードする API もあるので興味があれば試してみてください

Tips

{"error": "unsupported_grant_type", "error_description": "Invalid grant_type: "} が出る場合はクレデンシャルの JSON ファイルの情報が不足している可能性があるので再度ダウンロードし直してください

参考サイト

0 件のコメント:

コメントを投稿