一眼レフで撮った写真をGoogle Photosに自動アップロード その2 サーバースクリプト設置

<2019年1月にPicasa Photos APIの廃止準備(2019年3月に完全停止)に伴い、ここに記載の方法では写真をアップロード出来なくなりました。Google Photos APIに変更する必要があります。が、本ブログではまだbashスクリプトによるアップロード方法を模索中(勉強中)です。>

 

その1では、PQI AirCardを自宅のWiFiで使うための設定を行いました。

f:id:naoyukinagano:20171031211815j:plain

 

今回は、サーバー側の設定をしていきます。

サーバーで実現することは、

PQI AirCardをIPアドレスで死活監視。

・活性後、PQI AirCard内の画像のうち、JPGだけをFTPで取得。その後に重複取得しないようにJPEGファイル名称をとあるファイルにアペンド(追加)

Google APIの認証情報(CLIENT ID,CLIENT SECRET,REFRESH TOKEN)に基づき、ACCESSS TOKENを取得します。

Google PhotosのアルバムIDに対してACCESS TOKENで認証し、JPEGをアップロードします。

・アップロードが終わったら、撮影日ごとにフォルダを作成してファイルを移動。

これをずーっとループでやっていきます。

 

まずは、Google APIの認証情報を取得します。Googleアカウント(Gmailアドレス)があることが前提です。

こちらに書いてあることをそのままやることで、Google Photos用の認証情報が全て得られます。

そう、パクりです。

qiita.com

デベロッパーコンソールにアクセスします。

https://console.developers.google.com

私は既に一度アクセスしているので、こんな画面になります。

f:id:naoyukinagano:20171031233327p:plain

おそらく、先に適当なプロジェクト名を指定しなければならないと思います。

私の場合はGooglePhotosAPIとつけていまして、上部に表示されています。

左側の「認証情報」をクリックします。

f:id:naoyukinagano:20171031233917p:plain

大きく青く「認証情報を作成」と出ていますね。ここから「クライアントIDの作成」を選びます。

f:id:naoyukinagano:20171031234335p:plain

アプリケーションの種類は「その他」を選び、適当な名前をつけます。

私の場合は「GooglePhotosUploader」とつけてみました。

「作成」を押します。

f:id:naoyukinagano:20171031235224p:plain

このように、クライアントIDとクライアントシークレットが取得できました。これをどこかにコピーしておきます。

(Googleデベロッパーコンソールサイト上でももう一度確認できます。)

 

続いては、Webサーバー上のでの作業です。

これこそ、先のtamanobiさんの記事の通りに実行します。

まずは、クライアントIDとクライアントシークレットを使ってオーソリゼーションコードを取得します。

私はGentoo Linuxをサーバに使っている変な人なので、Gentoo上で作業します。(といってもほとんどのLinuxディストリビューションで変わらない作業だと思いますが)

作業ディレクトリとして、/home/homephotoとしました。

ここにget_authorization_code.shという名前でtamanobiさんのスクリプトを作成します。

# mkdir /home/homephoto

# cd /home/homephoto

# nano get_authorization_code.sh

すみません。なんの芸もなく、tamanobiさんのスクリプトを引用させていただきます。

下記のYOUR_CLIENT_IDは、上記で取得したクライアントIDに置き換えてください。

CLIENT_ID="YOUR_CLIENT_ID"
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob"
SCOPE="https://picasaweb.google.com/data/"
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"

これは、認証ページにアクセスするためのURLを生成するスクリプトです。

実行権限を与えて、実行します。

# chmod +x get_authorization_code.sh

# ./get_authorization_code.sh

こんなURLが出てきます。

はい、このYOUR_CLIENT_IDの部分を書き換えてブラウザでアクセスしても同じことです。はい。

https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=$CLIENT_ID redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://picasaweb.google.com/data/&access_type=offline

 

こんな画面が出てきますので、許可しましょう

f:id:naoyukinagano:20171104215550p:plain

 許可を押すと、味気なくオーソリゼーションコードが表示されますので、どこかにコピペしましょう。

f:id:naoyukinagano:20171104215918p:plain

 

続いて、リフレッシュトークンとアクセストークンを取得します。

この2つのトークンのうち、リフレッシュトークンは明示的に変更をしない限り変わりませんが、アクセストークンの有効期限が3600秒です。

またまたtamanobiさんの記事をパクります。

スクリプトを作りましょう。

# nano get_access_token_and_refresh_token.sh

スクリプトの中身は次の通りですが、YOUR_CLIENT_ID、YOUR_CLIENT_SECRET、先ほど取得したオーソリゼーションコードをYOUR_CODEに入れ替えましょう。

CLIENT_ID="YOUR_CLIENT_ID" # クライアントID
CLIENT_SECRET="YOUR_CLIENT_SECRET" # クライアントシークレット
AUTHORIZATION_CODE="YOUR_CODE" # 認可コード
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" # HTTPサーバを起動しなくてもAuthorization_Codeを取得できる

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

で、実行権限を付与して実行します。

# chmod +x get_access_token_and_refresh_token.sh

# ./get_access_token_and_refresh_token.sh

そうするとJSON形式でリフレッシュトークンとアクセストークンが取得できます。

このリフレッシュトークンが大事になります。

 

そして、Google Photosの特定のアルバムにアップロードしたいので、アルバムIDを調べましょう。

ブラウザで次のURLにアクセスします。

USER_IDの部分はGoogleのユーザーIDに書き換えてください。ユーザーIDはGmailアドレスの@より前ですね。

https://picasaweb.google.com/data/feed/api/user/USER_ID

そうすると、アルバムのリストが現れます。

f:id:naoyukinagano:20171104222659p:plain

アルバムのリンクURLを見ると、次のような法則になっています。

https://picasaweb.google.com/"Picasaのユーザー識別"/"アルバムID"?locked=true

このアルバムIDを控えておきます。

 

さて、ここからはオリジナルだったりtamanobiさんのスクリプトの改変だったりです。

 

まず、PQI AirCardの死活監視を行い、アクセスできたらFTPでJPGだけをGETします。

GETし終わったら、同じファイルをGETしないよう、GETしたファイルリストを作ります。

アクセストークンを取得し、JPEGファイルをGoogle Photosにアップロードします。

その後、exiftoolというアプリケーションを使って、年月日(YYYYMMDD)のディレクトリに分けて保存します。

というのを繰り返します。

お気づきの方もいらっしゃると思いますが、デジカメでファイルを連番で記録するようにしていると、XXX_9999.JPGの次は新しくディレクトリを作成し、たとえばDCIM/200CANONの次はDCIM/201CANONに保存されます。

もし10000枚目だとしたらあきらめてください((^^ゞ)

ちなみに、DCIM/200CANONディレクトリの中身を全て消去し、DCIM/201CANONのディレクトリ自体を消去すると、次からデジカメはDCIM/200CANONに撮影写真を保存していきます。

あと、なぜDCIM200CANONなのかというと、PQI AirCardにはDCIM/199WIFIという制御用ディレクトリがあるため、200CANONにしないと199WIFIに保存してしまいます。制御用のJPEGもダウンロードしてしまうことになるためです。

 

getophoto.shという名前のスクリプトにしました。

#/bin/bash

PQI_AIR_IP="192.168.xxx.xxx"

PQI_DIR="sd/DCIM/200CANON"

USER_ID="USER_ID"

CLIENT_ID="YOUR_CLIENT_ID"

CLIENT_SECRET="YOUR_CLIENT_SECRET"
REFRESH_TOKEN="YOUR_REFRESH_TOKEN"

ALBUM_ID="YOUR_ALBUM_ID"

 

#自身のディレクトリに移動
cd `dirname $0`

#プロセス番号をpid.txtに保存

echo $$ > pid.txt

 

#ここからループ

while :

do

 

#PQI AirCarにhttp出来るかどうか試す
ALIVE=$(/usr/bin/wget -nv --spider --timeout 60 -t 1 http://${PQI_AIR_IP}/ 2>&1 | grep -c '200 OK')

 

#PQI AirCadが生きていた場合の処理

if [ $ALIVE -eq 1 ]; then

 

#過去にGETしたJPGファイルのリストdonelist.txtを読み込む。念のため改行を削る

DONELIST=`cat donelist.txt | sed -e ':loop; N; $!b loop; s/\n//g'`

 

#FTPでJPGをGET
/usr/bin/wget -m -q -N --reject=${DONELIST} --no-host-directories --no-directories -A .JPG ftp://${PQI_AIR_IP}/${PQI_DIR}

 

#GETしたJPGを改行などを削除してdonelistに保存

JPGLIST=`/bin/ls | grep .JPG`
if [ -n "$JPGLIST" ]; then
/bin/echo "," >> donelist.txt
/bin/ls | grep .JPG | sed -e ':loop; N; $!b loop; s/\n/,/g' >> donelist.txt
sed -i -e ':loop; N; $!b loop; s/\n//g' donelist.txt
fi

 

#アクセストークン取得
curl -s --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 > token.JSON
ACCESS_TOKEN=`cat token.JSON | jq -r '.access_token'`


ENDPOINT="https://picasaweb.google.com/data/feed/api/user/${USER_ID}/albumid/${ALBUM_ID}?access_token=${ACCESS_TOKEN} "

 

DIR="./" # FTPでGETしたJPGファイルが保存してあるディレクトリを指定

IFS_BAK=${IFS}
IFS="
"

 

#ファイル数だけループ
FILES=`ls -1 ${DIR}`
for FILE in ${FILES}
do

 

#MIMEタイプの指定
if [ "${FILE}" != "" ]; then
TYPE="UNKNOWN"
case "${FILE}" in
*\.bmp) TYPE="image/bmp";;
*\.JPG | *\.jpg) TYPE="image/jpeg";;
*\.png) TYPE="image/png";;
*) TYPE="UNKNOWN";;
esac
# echo "$TYPE $FILE $LENGTH"
if [ "${TYPE}" = "UNKNOWN" ]; then
continue;
fi
LENGTH=`ls -l "${DIR}/${FILE}" | tail -n1 | sed -E 's/ +/ /g' | cut -d' ' -f5`

 

#ファイルをアップロード

curl -s -XPOST "${ENDPOINT}" \
-H "Content-Type:${TYPE}" \
-H "Content-Length:${LENGTH}" \
-H "Slug:${FILE}" \
--data-binary "@${DIR}/${FILE}" > /dev/null
sleep 1
fi
done
IFS=$IFS_BAK

 

#ファイルのアップロードが全て終わったら、撮影日でYYYYMMDDというディレクトリを作ってファイルを移動
/usr/bin/exiftool -q '-Directory < CreateDate' -d %Y%m%d *.JPG
fi

sleep 10
done

 

これも実行権限をつけて実行しましょう。

chmod +x getophoto.sh

./getophoto.sh &

 

これで、サーバーと同じネットワークセグメントにいる限り、つまり、室内では、撮影する度にGoogle Photosの指定したアルバムにアップロードされます。

たしかGoogle Photosの仕様で、1アルバム1000枚までしか保存できないはずですので、上限に達したら新しいアルバムに変えましょう。