Google Apps ScriptでPhotos Library APIを触ってみる【GAS】

2018年5月にこれまでGoogleが公開していたPicasa Web Albumが終了し、Google Photosに移行しました。これに伴いAPIも変更され、Google Photos Library APIに変更されています。写真データを呼び出すだけでなく、アップロードや変更、アルバムの作成などがAPIからコントロール可能になっています。

Google Apps Scriptではそのままでは使えないAPIですが、OAuth2.0認証をすることで利用することが可能です。スプレッドシート上などに画像を挿入する場合、標準機能でGoogle Photosの写真が出せるので、こちらはどちらかというとアプリケーションとして管理や魅せ方などのガジェットで使いみちがあるんじゃないかなぁと思います。

今回使用するスプレッドシート

カルーセルスライダー入れようとしてまだ、入れてないです・・・スライドはしません・・・

概要

Google Photos Library APIはサービスアカウントではなく、通常のクライアントIDとシークレットで認証をしてアクセスすることが可能です。標準でGASからアクセスする手段はないですが、UrlfetchAppのGETとPOSTだけでアクセスは可能になっていますので、今回は認証部分とアルバムIDの取得、画像リストの取得を行ってみたいと思います。

注意点として、アルバムIDはPhotosのアルバムのURLに含まれている文字列とは異なり、スクリプトからアルバム一覧を取得し、idという値で取得したものがアルバムIDになる点です。一度、リストを取得しないとわかりません。また、画像のURLは2種類あって、通常は、baseUrlの値を利用します。この2点を踏まえて実装が必要になります。

また、スコープは何種類かありますが、通常見せるだけであれば、https://www.googleapis.com/auth/photoslibrary.readonlyでOKです。アップロードや編集も行なう場合には、https://www.googleapis.com/auth/photoslibraryを利用します。

事前準備

Cloud Consoleにプロジェクトを連結する

今回は、Google Cloud ConsoleにあるPhotos Library APIを利用するため、Google Cloud ConsoleとGASのプロジェクトを連結させる必要性があります。以下の手順でプロジェクトを移行しましょう。

  1. Google Cloud Consoleを開く
  2. 左上にある▼をクリックする
  3. ダイアログが出てくるので、新規プロジェクトを作るか?既存のプロジェクトを選択する。この時、G Suiteであれば選択元は「自分のドメイン」を選択する必要があります。
  4. プロジェクト情報パネルから「プロジェクト番号」をコピーする
  5. 対象のGoogle Apps Scriptのスクリプトエディタを開く
  6. リソース」⇒「Cloud Platform プロジェクト」を開く
  7. 4.で入手した番号をプロジェクトを変更のテキストボックスに入れて、プロジェクトを設定ボタンをクリックする
  8. 無事に移動が完了すればメッセージが表示されます。
  9. この時、元の自動作成されたプロジェクトはシャットダウンされて消えます。これで設定完了です。

図:プロジェクト番号が重要です

図:プロジェクトを他のプロジェクトに紐付けしました。

Photos Library APIを追加する

Google Photos Library APIはOAuth2.0認証が必要なので、クライアントIDシークレットが必要になります。取得手順は以下の通り。これはソースコード内に記述が必要ですので、控えておきます。

  1. スクリプトエディタより「ファイル」⇒「プロジェクトのプロパティ」を開き、スクリプトIDを控えておく
  2. スクリプトエディタより「リソース」⇒「Googleの拡張サービス」をクリック
  3. Google Cloud Platform API ダッシュボードをクリックします。
  4. 左サイドメニューからAPIとサービスを開き、「APIとサービスを有効化」をクリックします。
  5. photosと検索すると、Photos Library APIがあるのでクリック。有効化をクリックします。
  6. 続けて、左サイドの認証情報をクリックします。
  7. 認証情報を作成をクリックします。
  8. OAuthクライアントIDを選択します。
  9. 次の画面では、「ウェブアプリケーション」を選択します。
  10. 承認済みの JavaScript 生成元」では、https://script.google.comを入力
  11. 承認済みのリダイレクト URI」には、控えておいた1.の「スクリプトIDをつなげた、値(例:https://script.google.com/macros/d/スクリプトID/usercallback)を入力して作成をクリック
  12. クライアントIDクライアントシークレットが表示されるので、控えておく。
  13. ソースコード内に12.の内容を記述する。

図:スクリプトIDを取得しておきます。

図:Photos Library APIを有効化

図:認証情報を作成中の画面

OAuth2.0認証ライブラリの追加

今回のサービスは、OAuth2.0認証が必要です。以下の手順でOAuth2 for Apps Scriptライブラリを追加しましょう。

  1. スクリプトエディタを開きます。
  2. メニューより「リソース」⇒「ライブラリ」を開きます。
  3. ライブラリを追加欄に「1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF」を追加します。
  4. 現時点ではバージョンは30が最新ですので、それを選択しておきます。
  5. 保存ボタンを押して完了

これで、OAuth2.0認証にまつわる様々な関数を手軽に利用できるようになります。

図:ライブラリを追加した様子

ソースコード

GAS側のコード

認証系のスクリプト

//認証用各種変数
var tokenurl = "https://accounts.google.com/o/oauth2/token"
var authurl = "https://accounts.google.com/o/oauth2/auth"
var clientid = 'ここにクライアントIDを入力する';
var clientsecret='ここにクライアントシークレットを入力する';
var scope = "https://www.googleapis.com/auth/photoslibrary"

function startoauth(){
  //UIを取得する
  var ui = SpreadsheetApp.getUi();
  
  //認証済みかチェックする
  var service = checkOAuth();
  if (!service.hasAccess()) {
    //認証画面を出力
    var output = HtmlService.createHtmlOutputFromFile('template').setHeight(310).setWidth(500).setSandboxMode(HtmlService.SandboxMode.IFRAME);
    ui.showModalDialog(output, 'OAuth2.0認証');
  } else {
    //認証済みなので終了する
    ui.alert("すでに認証済みです。");
  }
}

//アクセストークンURLを含んだHTMLを返す関数
function authpage(){
  var service = checkOAuth();
  var authorizationUrl = service.getAuthorizationUrl();
  var html = "<center><b><a href='" + authorizationUrl + "' target='_blank' onclick='closeMe();'>アクセス承認</a></b></center>"
  return html;
}

//認証チェック
function checkOAuth() {
  return OAuth2.createService("PhotosAPI")
    .setAuthorizationBaseUrl(authurl)
    .setTokenUrl(tokenurl)
    .setClientId(clientid)
    .setClientSecret(clientsecret)
    .setCallbackFunction("authCallback") //認証を受けたら受け取る関数を指定する
    .setPropertyStore(PropertiesService.getScriptProperties())  //スクリプトプロパティに保存する
    .setScope(scope)
    .setParam('login_hint', Session.getActiveUser().getEmail())
    .setParam('access_type', 'offline')
    .setParam('approval_prompt', 'force');
}

//認証コールバック
function authCallback(request) {
  var service = checkOAuth();
  Logger.log(request);
  var isAuthorized = service.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput("認証に成功しました。ページを閉じてください。");
  } else {
    return HtmlService.createHtmlOutput("認証に失敗しました。");
  }
}

//ログアウト
function reset() {
  checkOAuth().reset();
  SpreadsheetApp.getUi().alert("ログアウトしました。")
}

  • GETで通信して、アルバム一覧データを取得しています。
  • このコードはテスト用で、アルバム一覧を取得する為のコードです。
  • このコードで返ってくる各アルバムについているidを見つけて、次のコードで利用します。
  • resultの中身は以下のような感じになります。

アルバム一覧を取得するコード

//アルバム一覧を取得する
function photoalbum(){
  //OAuth認証情報を取得
  var service = checkOAuth();

  //Access Tokenを取得
  var accessToken = service.getAccessToken();

  //POSTで通信
  var url = "https://photoslibrary.googleapis.com/v1/albums";
  var response = UrlFetchApp.fetch(url, {
    method: 'GET',
    headers: {
      Authorization: 'Bearer ' + accessToken
    },
    contentType: "application/json",
    muteHttpExceptions: true
  });
  
  //一覧データを取得する
  var result = JSON.parse(response.getContentText());

}
  • GETで通信して、アルバム一覧データを取得しています。
  • このコードはテスト用で、アルバム一覧を取得する為のコードです。
  • このコードで返ってくる各アルバムについているidを見つけて、次のコードで利用します。
  • resultの中身は以下のような感じになります。
{albums=
    [{
        mediaItemsCount=1, 
        coverPhotoMediaItemId=これは利用しないIDです, 
        coverPhotoBaseUrl=アルバムへの直リンクURL, 
        id=これが利用するアルバムIDです。, 
        title=アルバム・タイトルが入っています, 
        productUrl=アルバムページへのURL
    }] 
}

アルバムの画像一覧を取得するコード

//アルバムIDを指定する
var albumid = "ここにアルバムのIDを入れる";

//アルバム内の画像一覧
function photolist(){
  //OAuth認証情報を取得
  var service = checkOAuth();

  //Access Tokenを取得
  var accessToken = service.getAccessToken();

  //オプション指定(アルバムIDの指定など)
  var payload = {
      "pageSize":"100",
      "albumId": albumid
    };

  //POSTで通信
  var url = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
  var response = UrlFetchApp.fetch(url, {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + accessToken
    },
    contentType: "application/json",
    payload : JSON.stringify(payload),
    muteHttpExceptions: true
  });
  
  //一覧データを取得する
  var result = JSON.parse(response.getContentText());
  Logger.log(result);
  
  //値を返す
  return response.getContentText();
  
}
  • 前項で得たアルバムIDを入力しておきます。
  • Access Tokenを取得し、POSTで通信して一覧を取得します。
  • その際にパラメータとして、payloadを指定しています(pageSizeとここでアルバムIDを指定します)
  • 画像リスト一覧は以下のような形で返ってきます。
{
    mediaItems=[{
        baseUrl=写真への直リンクURL, 
        filename=ここにファイル名が入ってる, 
        mediaMetadata={
            creationTime=撮影日, 
            width=画像の横のサイズ, 
            photo={
                apertureFNumber=F値, 
                cameraModel=カメラのモデル, 
                cameraMake=カメラのメーカー 
                isoEquivalent=ISO感度, 
                focalLength=焦点
            }, 
            height=画像の縦の高さ
        }, 
        id=画像のID, 
        mimeType=image/jpeg, 
        productUrl=画像のページへのURL
    }]
}

ウェブアプリケーション側のコード

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>

    <style>
      .grid {
          width: 100%;
          max-width: 960px;
          margin: 0 auto;
      }
      .grid-item {
          margin: 10px;
      }
    </style>
    <script>
      //Google Photoライブラリを取得して反映
      google.script.run.withSuccessHandler(onSuccess).photolist();
      
      function onSuccess(data){
        //JSONを取得する
        var json = JSON.parse(data);
      
        //mediaitemsのlengthを取得する
        var length = json.mediaItems.length;
        
        
        //ループでHTMLを生成
        var html = "";
        
        for(var i = 0;i<length;i++){
          //画像の直リンクURLを取得する
          var imgurl = json.mediaItems[i].baseUrl;
          
          //ファイル名を取得する
          var filename = json.mediaItems[i].filename;
        
          //HTMLを生成
          html = html + "<div class='grid-item'>"
                      + "<div class='grid-item_image'>" + "<img src='" + imgurl + "' width='300' height='300'></div>"
                      + "<p class='grid-item_text'>" + filename + "</p></div>"
        }
        
        //生成したデータを反映する
        var elem = document.getElementById("tomato");
        elem.innerHTML = html;

        //monsory適用
        var $grid = $('.grid').masonry({
          columnWidth: 80
        });

      }
    </script>
    
  </head>
  <body>
    <div class="grid" id="tomato">ロード中...</div>
  </body>
</html>

使い方と実行結果

使い方

今回は手抜きですが、使い方には手順があります。Client IDやSecret、およびアルバムIDをソースコード内に記述したら、以下の手順で認証が必要です。

  1. startoauth()を実行する
  2. 認証のウィンドウが出てくるので、認証をする
  3. Access Tokenがユーザプロパティに登録される
  4. スクリプトエディタの「公開」⇒「ウェブアプリケーションとして導入」。
  5. 色々設定して公開すると、最後がexecのURLが取得できる。表示するとウェブアプリとしてGoogle Photosの写真が出てくる

特に、2.が重要です。

図:Googleフォトライブラリの表示と管理が出てきた

データをウェブアプリとして表示してみた

直リンクURLとファイル名が取得できるので、色々なライブラリで色々な魅せ方が出来ると思います。今回のサンプルは結構手抜きなので、色々と追加しないと、アプリケーションとしてはちょっとアレだと思います。

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)