GoogleAppScriptでPhoto Library APIを使用してアルバム一覧をSpreadsheetに書き出す






## 1. OAuth認証ライブラリを追加する
### 1-1. OAuth2ライブラリにProject追加
Google Photo APIは基本的に認証にOAuthを用いています。

Oauthについてはこちらの記事が分かりやすいと思います。
https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be

まずはOAuthをGASで簡単に使えるように「OAuth2 for Apps Script」をプロジェクトに追加しましょう。

1.メニューの「リソース > ライブラリ」を選択
<img width="678" alt="スクリーンショット 2020-02-21 15.11.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/63b50f35-3abb-8989-ef59-5f1623c2cde6.png">


2.ID「1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF」を入力して「追加」を押す(バージョンは最新のものを選択)
<img width="696" alt="スクリーンショット 2020-02-21 15.13.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/cb819808-ef42-e0e4-5ed5-cfd9632b87ac.png">


3.「保存」を押してダイアログを閉じる

## 2.Photos Library APIの有効化
### 2.1 Google Cloud PlatformでProjectを新規作成
### 2.2 APIを有効化
<img width="820" alt="スクリーンショット 2020-02-21 16.31.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/52cd40bb-88a9-3d13-5d47-af970c279b52.png">

<img width="1307" alt="スクリーンショット 2020-02-21 16.32.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/ee09092c-cf13-feb0-4b0c-bd72022f7500.png">


### 2.3 Oauthの認証情報を追加する
#### 「認証情報 > OAuthクライアントID」を選択
<img width="1040" alt="スクリーンショット 2020-02-21 16.49.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/32c55338-fd04-480a-e3db-bd8567191797.png">

### 認証情報を登録する

<img width="671" alt="スクリーンショット 2020-02-21 16.51.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/9df0fc5b-4e91-f3b6-4b24-cdf58ca4db1e.png">

### ①ウェブアプリケーションを選択

#### ②承認済みのJavaScript 生成元
`https://script.google.com`

#### ③承認済みのリダイレクトURI
`https://script.google.com/macros/d/スクリプトID/usercallback`
スクリプトIDはスクリプトエディターにて「ファイル > プロジェクトのプロパティー」から確認できます。
<img width="624" alt="スクリーンショット 2020-02-21 16.37.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/f451f130-acc3-1958-91e0-9a435e677748.png">

### 2.4 クライアントIDとクライアントシークレットを控える
クライアントIDとクライアントシークレットは控えておきます
<img width="1307" alt="スクリーンショット 2020-02-21 16.42.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/5908b661-26f4-e1fc-65a7-08506288c683.png">

## 3.認証部分のGASを記述する
### 3.1 Spreadsheet上にボタンを作成する
アルバム一覧を取得、認証をリセットするという２つのボタンを用意します。

```js:main.gas
//スプレッドシートを開いた時に呼ばれる関数
function onOpen() {
  SpreadsheetApp
  .getUi()
  .createMenu('スクリプト')
  .addItem('アルバム一覧を取得', 'getAlbumInformation')
  .addItem('認証のリセット', 'reset')
  .addToUi()
}

```

実行結果
<img width="852" alt="スクリーンショット 2020-02-22 14.36.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/50285e3c-59f0-f040-9206-b4c20f2d10bf.png">


### 3.2 OAuth認証の確認
OAuth認証が通っているかどうかの確認をします。
もし通っていなかったら認証画面を出力します。

#### 3.2.1 認証HTMLファイルの作成
<img width="867" alt="スクリーンショット 2020-02-22 14.41.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/4f2a017f-9319-1cce-99c5-2d24812615bb.png">


```html:template.html
<!DOCTYPE html>
<html>
  <head>
    <style>
      center {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100vh;
      }
      center > b > a {
        text-decoration: none;
        color: aliceblue;
        background-color: cadetblue;
        padding: 30px;
        border-radius: 15px;
      }
    </style>
    <base target="_top">
  </head>
  <body style="margin: 0;">
    
  </body>
</html>
```
#### 3.2.2 認証部分の作成

```js:main.gs
//PhotoLibrary用定数
var baseUrl = 'https://photoslibrary.googleapis.com/v1/albums'
var accessToken = ''

//認証用各種変数
var tokenurl = "https://accounts.google.com/o/oauth2/token"
var authurl = "https://accounts.google.com/o/oauth2/auth"
var clientid =    '〇〇〇〇';// 先程作成したclientIdを用いる
var clientsecret= '〇〇〇〇';// 先程作成したclientsecretを用いる
var scope = "https://www.googleapis.com/auth/photoslibrary"// 使いたいAPIによって変える

//共通定数
var currentSpreadheet = SpreadsheetApp.getActive();

// ボタンを押した時に呼ばれる関数
function getAlbumInformation() {
  var ui = SpreadsheetApp.getUi()
  
  //1. 認証の確認
  var service = checkOAuth()
  if (!service.hasAccess()) {
    //認証画面を出力
    var output = HtmlService.createHtmlOutputFromFile('template').setHeight(310).setWidth(500).append(authpage()).setSandboxMode(HtmlService.SandboxMode.IFRAME)
    ui.showModalDialog(output, 'OAuth2.0認証')
  } else {
    //2. 認証されている場合、Albumを取得する
    getAllAlbums() //後ほど記述します。
  }
}

//アクセストークン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();
  var isAuthorized = service.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput("認証に成功しました。ページを閉じて再度実行ボタンを押してください");
  } else {
    return HtmlService.createHtmlOutput("認証に失敗しました。再度お試しください");
  }
}
 
```

この段階でボタンを押すと次のような認証ボタンが出てきます。
<img width="533" alt="スクリーンショット 2020-02-22 14.50.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144044/51fefcab-9030-301b-e5f9-a1a70f95d17f.png">

ここでアクセス承認ボタンを押して認証を通すことができます。

## 4.アルバム範囲を取得するGASを書く
```js:main.gas

//認証が通っている場合呼ばれる関数
function getAllAlbums() {
  // OAuth認証情報を取得
  const service = checkOAuth()
  // Access Tokenを取得
  accessToken = service.getAccessToken()

  //1. アルバム一覧を取得する
  var rawAlbums = getAlbums()
  if (!rawAlbums) {
    return Browser.msgBox('アルバムの取得に失敗しました')
  }
  
  //3.アルバムを二元配列に変換する
  var classAlbums = rawAlbums.map(function(e) {
    // e['title']でアルバムの名前を取得できる
    return [e['title']]
  })
  
  //4.セルに書き出す
  albumSheet.getRange(1, 1, classAlbums.length).setValues(classAlbums)
}

//アルバム一覧を取得する
function getAlbums(){  
  //defalutで20件しか取得できないのでparamaterを設定して50件取得する
  var response = UrlFetchApp.fetch(baseUrl + '?pageSize=50', {
    method: 'GET',
    headers: {
      Authorization: 'Bearer ' + accessToken
    },
    contentType: "application/json",
    muteHttpExceptions: true
  });
  var responseCode = response.getResponseCode()
  // 50件しか取得できない
  if (responseCode === 200) {
    var result = JSON.parse(response.getContentText())
    return result['albums']
  } else {
    return null
  }
  
}
```

以上でアルバムの名前をシートに書き出すことができました！


## まとめ
```js:main.gas

//PhotoLibrary用定数
var baseUrl = 'https://photoslibrary.googleapis.com/v1/albums'
var accessToken = ''

//認証用各種変数
var tokenurl = "https://accounts.google.com/o/oauth2/token"
var authurl = "https://accounts.google.com/o/oauth2/auth"
var clientid =    '〇〇〇〇';// 先程作成したclientIdを用いる
var clientsecret= '〇〇〇〇';// 先程作成したclientsecretを用いる
var scope = "https://www.googleapis.com/auth/photoslibrary"// 使いたいAPIによって変える

//共通定数
var currentSpreadheet = SpreadsheetApp.getActive();

// ボタンを押した時に呼ばれる関数
function getAlbumInformation() {
  var ui = SpreadsheetApp.getUi()

  //1. 認証の確認
  var service = checkOAuth()
  if (!service.hasAccess()) {
    //認証画面を出力
    var output = HtmlService.createHtmlOutputFromFile('template').setHeight(310).setWidth(500).append(authpage()).setSandboxMode(HtmlService.SandboxMode.IFRAME)
    ui.showModalDialog(output, 'OAuth2.0認証')
  } else {
    //2. 認証されている場合、Albumを取得する
    getAllAlbums() //後ほど記述します。
  }
}

//アクセストークン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();
  var isAuthorized = service.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput("認証に成功しました。ページを閉じて再度実行ボタンを押してください");
  } else {
    return HtmlService.createHtmlOutput("認証に失敗しました。再度お試しください");
  }
}


function getAllAlbums() {
  // OAuth認証情報を取得
  const service = checkOAuth()
  // Access Tokenを取得
  accessToken = service.getAccessToken()

  //1. アルバム一覧を取得する
  var rawAlbums = getAlbums()
  if (!rawAlbums) {
    return Browser.msgBox('アルバムの取得に失敗しました')
  }
  
  //3.アルバムを二元配列に変換する
  var classAlbums = rawAlbums.map(function(e) {
    // e['title']でアルバムの名前を取得できる
    return [e['title']]
  })
  
  //4.セルに書き出す
  albumSheet.getRange(1, 1, classAlbums.length).setValues(classAlbums)
}

//アルバム一覧を取得する
function getAlbums(){  
  //defalutで20件しか取得できないのでparamaterを設定して50件取得する
  var response = UrlFetchApp.fetch(baseUrl + '?pageSize=50', {
    method: 'GET',
    headers: {
      Authorization: 'Bearer ' + accessToken
    },
    contentType: "application/json",
    muteHttpExceptions: true
  });
  var responseCode = response.getResponseCode()
  // 50件しか取得できない
  if (responseCode === 200) {
    var result = JSON.parse(response.getContentText())
    return result['albums']
  } else {
    return null
  }
  
}
```

```html:template.html
<!DOCTYPE html>
<html>
  <head>
    <style>
      center {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100vh;
      }
      center > b > a {
        text-decoration: none;
        color: aliceblue;
        background-color: cadetblue;
        padding: 30px;
        border-radius: 15px;
      }
    </style>
    <base target="_top">
  </head>
  <body style="margin: 0;">

  </body>
</html>
```