kimagre inrash

感想を書きます

Raspberry Pi ネコ観察カメラの運用をGooglePhotoAPIsに移行する

time 2018/05/28

ネコカメラ

raspi0 (1).jpg
以前までの運用方法。
  1. raspberry Pi から googleDrive に撮影したファイルを上げる
  2. IFTTT によって googleDrive から googlePhoto にファイルを移動
  3. googleDriveから【手動】でファイルを消す
こんな感じでした。
一応これでも運用できていたのですが、IFTTTが機能しないタイミングがあったり、【手動】でファイル削除するのが手間だったのでもう少し軽減できないか考えたところ、直接googlePhotoに上げれれば解決じゃない?という結論に。

Google Photo APIsを使う

以前はgooglePhoto用のAPIは公開されていなくて、前サービスであるPicasaのAPIを使う方法が一般的でした。
Google Photosの無料容量15GBを有効活用!cURLでGoogle Photosへ画像アップロードするには?
調べてみたところ、GooglePhotoAPIが用意されていたので使ってみることにしました。
(※2018/05/28現在、APIは試用版で一部制限あり)
Google Photo API

初期設定(サーバー側)

googlePhotoのAPIを使うには最終的に【アクセストークン】が必要になります。

まずはGoogle API Console にアクセスしてGoogleアカウントでログイン。

◆プロジェクトの作成
APIとサービス利用にはプロジェクトが必要。
「プロジェクトを作成」から新たに作成します。
(既に作成してあるプロジェクトに追加する場合は選択すればOK)
◆API追加
APIを利用できるように追加する。
  1. 「ライブラリ」から「photo」などで検索。
  2. 【Photos Library API】を選択。
  3. 「有効にする」
◆クライアントIDとクライアントシークレットの取得
このAPIの利用には「認証情報」が必要なので作成します。
「認証情報」の設定には「OAuth同意画面」の設定が必要なので先にそちらをやります。
必須なのは「ユーザーに表示するサービス名」。
特に外部に公開する予定はないので自分がわかりやすい名前にしておけばよいかと。
設定ができたら最後に以下の設定。
  1. 認証情報から「認証情報を作成」。
  2. 「OAuthクライアントID」を選択。
  3. アプリケーションの種類は「その他」、名前は任意の名前で。
  4. 作成されると「クライアントID」と「クライアントシークレット」を取得できる。
サーバー側の設定は以上。次はクライアント側。

初期設定(クライアント側)

今回クライアント側はシェルからアクセスするのを想定。
◆jqコマンドのインストール
jqコマンドはJSON形式のデータを簡単に扱えるようになるコマンド。
APIから返ってくるデータもJSON形式なのでそれをそのまま使えるようにインストールします。
jqコマンドをCentOSで使う
上記サイトを参考にインストール。
と思ったらちょっと古い記事らしく、以下のコマンドで簡単にインストール出来るらしい。
$ sudo apt-get install jq
◆認可コードの取得
先ほどサーバー側で設定した「クライアントID」を使うことで「認可コード」を取得できる。
「認可コード」取得用にURLを作成してブラウザでアクセス、承認することで発行される。
◆アクセストークンの取得
「クライアントID」「クライアントシークレット」「認可コード」を使って「アクセストークン」を取得。
JSON形式で返ってきます。
また、後ほど必要になる「リフレッシュトークン」についてもJSON内に記述があります。
上記、認可コードの取得~アクセストークンの取得をまとめたのが以下のシェル。
自分が使いやすいように特化してるので注意。
(※json形式のデータ保存のためにjsonフォルダが必要だったり・・)
クライアントID(Google API Consoleの認証情報参照)
クライアントシークレット(Google API Consoleの認証情報参照)
Scope
(書き込みのみの場合)
https://www.googleapis.com/auth/photoslibrary.appendonly
認可コード(以下のシェルで生成されるURLにアクセスすると発行される)
auth.sh
#!/bin/bash
# シェルの位置にカレントディレクトリを移動させる
cd `dirname ${0}`
CLIENT_ID="" # クライアントID
CLIENT_SECRET="" # クライアントシークレット
SCOPE="" # スコープ
# クライアントIDを取得
echo -n "クライアントIDを入力:"
read CLIENT_ID
# クライアントシークレット
echo -n "クライアントシークレットを入力:"
read CLIENT_SECRET
# クライアント情報を保存(client.jsonに保存)
echo "{\n \"client_id\": \"${CLIENT_ID}\",\n \"client_secret\": \"${CLIENT_SECRET}\"\n }" > json/client.json
# スコープを取得
echo -n "Scopeを入力:"
read SCOPE
# 認証用URLを表示
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" # HTTPサーバを起動しなくてもAuthorization_Codeを取得できる
AUTHORIZATION_CODE="" # 認可コード
echo "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&scope=$SCOPE&access_type=offline"
echo -n "上記URLをブラウザでアクセスし、認可コードを入力:"
read AUTHORIZATION_CODE
# トークンの取得(refresh_token.jsonに保存)
curl --data "code=${AUTHORIZATION_CODE}" --data "client_id=${CLIENT_ID}" --data "client_secret=${CLIENT_SECRET}" --data "redirect_uri=${REDIRECT_URI}" --data "grant_type=authorization_code" --data "access_type=offline" https://www.googleapis.com/oauth2/v4/token > json/refresh_token.json
echo "設定完了!"
以上で初期設定は終了。
ちょっと気になってるのが上記シェルは「クライアントシークレット」をそのまま保存しちゃってる点。
シークレットのハズなんだけど、むき出し。
でもこれを保存しておかないと自動でアップロードとかが出来なくなるのでしょうがないかな・・
(メモリ上に展開しておいてそれをシェルで読み込むようにする???)
個人で作ってるものだし、今回はスルー。

アルバムを作成する

初期設定が終了したのでアルバムを作成してみる。
しかし、先ほど発行した「アクセストークン」は3600秒しか使えないため、それ以降は再発行が必要。
再発行には「アクセストークン」と一緒に届いた「リフレッシュトークン」を使えば、実現できます。
token.sh
#! /bin/bash
# シェルの位置にカレントディレクトリを移動させる
cd `dirname ${0}`
# リフレッシュトークン取得
REFRESH_TOKEN=`cat json/refresh_token.json | jq -r '.refresh_token'`
echo "REFRESH_TOKEN: ${REFRESH_TOKEN}"
# リフレッシュトークンからアクセストークン取得
CLIENT_ID=`cat json/client.json | jq -r '.client_id'`
CLIENT_SECRET=`cat json/client.json | jq -r '.client_secret'`
curl --data "refresh_token=${REFRESH_TOKEN}" --data "client_id=${CLIENT_ID}" --data "client_secret=${CLIENT_SECRET}" --data "grant_type=refresh_token" https://www.googleapis.com/oauth2/v4/token > json/access_token.json
以上のシェルで「アクセストークン」が発行されたのでこれを使ってアルバムを作成します。
アルバムは【マシン毎1日に1回、cronで作成】して運用します。
理由は・・
  • 1アルバム2000枚という上限がある
    • 1分1枚では24時間で最大1440枚なので2日分は無理
  • タイムラプス動画を作るにあたり、アルバム丸ごとダウンロードが便利だから
アルバムを作成してて気づいたいくつかの仕様は以下。
  • アルバム名は同じ名前が可能
  • アルバム操作するにはアルバムIDを使ってのみアクセスできる
    • なお、アルバム名からアルバムIDを取得する手段がない
      • アルバム作成時にアルバムIDをJSON形式で保存するようにしました
album.sh
#! /bin/bash
# シェルの位置にカレントディレクトリを移動させる
cd `dirname ${0}`
# リフレッシュトークンからアクセストークンを取得
sh token.sh
ACCESS_TOKEN=`cat json/access_token.json | jq -r '.access_token'`
echo "ACCESS_TOKEN: ${ACCESS_TOKEN}"
# アルバム作成
TITLE_A=`date "+%Y%m%d"`
TITLE_B=`uname -n`
ALBUM_TITLE="${TITLE_A}_${TITLE_B}"
echo "ALBUM_TITLE: ${ALBUM_TITLE}"
curl -H "Content-type: application/json" -H "Authorization: Bearer ${ACCESS_TOKEN}" -d '{"album": {"title": "'${ALBUM_TITLE}'"}}' -X POST "https://photoslibrary.googleapis.com/v1/albums" > json/album.json
内部でtoken.sh を呼び出して「アクセストークン」を取得するようにしています。

ファイルをアルバムにアップロードする

ファイルをアップロードすると「アップロードトークン」が発行されるのでそれを「アルバムID」と送ることでアルバム内にファイルが格納される。
upload.sh
#! /bin/bash
# シェルの位置にカレントディレクトリを移動させる
cd `dirname ${0}`
# リフレッシュトークンからアクセストークンを取得
sh token.sh
ACCESS_TOKEN=`cat json/access_token.json | jq -r '.access_token'`
echo "ACCESS_TOKEN: ${ACCESS_TOKEN}"
# ファイルをアップロードし、アップロードトークンを取得
FILE="sample.jpg"
UPLOAD_TOKEN=`curl -H "Content-type:application/octet-stream" -H "Authorization: Bearer ${ACCESS_TOKEN}" -H "X-Goog-Upload-File-Name: ${FILE}" -X POST "https://photoslibrary.googleapis.com/v1/uploads" --data-binary "@${FILE}"`
echo "UPLOAD_TOKEN: ${UPLOAD_TOKEN} "
# アルバムIDを取得し、ファイルをアルバムに追加
ALBUM_ID=`cat json/album.json | jq -r '.id'`
ITEM_DESC=`date "+%Y%m%d-%H%M%S"`
curl -H "Content-type: application/json" -H "Authorization: Bearer ${ACCESS_TOKEN}" -d '{"albumId": "'${ALBUM_ID}'", "newMediaItems":[{"description":"'${ITEM_DESC}'","simpleMediaItem":{"uploadToken": "'${UPLOAD_TOKEN}'"}}]}' -X POST "https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate"
これで完了。
上記シェルはファイルが sample.jpg になっているので実際はfswebcamやraspistillなどで撮影したファイルに差し替える必要有り。

いくつかの問題

googleDriveやIFTTTを使わずにアルバム作成~ファイルアップロードまでを自動化出来ました。
しかし、いくつか問題が。
◆googleアカウントのストレージを消費する
APIを通じてアップロードされたファイルは元の品質、元の解像度で保存される仕様のようです。
つまり、ストレージ容量を食うということ。
googleDriveからIFTTT経由でgooglePhotoに上げた場合はgoogleDrive側はストレージを消費しますが、googlePhoto側はストレージを消費してませんでした。
一応、googlePhoto上で「容量の解放」を行えば、ストレージ消費なしには出来ます。
しかし、都度対応が必要。
今まではgooglePhoto側に入ったことを確認したらgoogleDrive側のファイルを手動で消していましたが、
ファイルを消すのが結構手間でフォルダを残して中身だけ全削除というのが簡単に出来ませんでした。
(ファイルを頑張って選択したり、同期を使ってローカルファイルを全て消したものをサーバーに反映などいろいろやってはいましたが・・)
それに比べれば、容量食ってきたら「容量の解放」ボタンを押すだけで済むので手順は軽くなったと言えます。
◆試用版のため、リクエストに制限あり
現在提供されているGooglePhotoAPIsは試用版のため、1日2,500リクエストまで。
6時から18時の間、1分毎に画像アップロード。
12時間x60分=720リクエスト。
それが2台なので・・
720×2台=1440リクエスト
一応もう1台増やしても耐えれますね。

まとめ

今回、初めてGoogleAPIを触りました。
トークンがたくさん出てきて最初はワケがわかりませんでしたが、無料でここまで使えるのは中々いい感じです。
数日運用してみてまた感想を書こうと思います。
トークンまとめ
アクセストークンGooglePhotoAPIを使うために必要なトークン。
有効期限は3600秒。
リフレッシュトークン上記アクセストークンを再取得するのに必要なトークン。
最初の認可コードを入力するところで入手できる。
アップロードトークンファイルをアップロードすると発行されるトークン。
有効期限は1日。
このトークンをメディアアイテムとして登録することでPhoto上で表示されるようになる。
(その手順をしないと有効期限が切れたときに消失する)
また、登録の際にアルバムと紐付けることでアルバム内にファイルが格納される

前後記事

Raspberry Pi Zero W + カメラモジュール でネコ観察カメラ2代目を作る
Raspberry Pi がタブレットになった!RasPad を申し込みました