FlashAir(W-04)の画像をGoogle Photos(Picasa Web)のアルバムにアップロードするスクリプトを改善 その2
以前、FlashAir(W-04)の画像をサーバー側のスクリプトを使って一時的にダウンロード、リサイズ、Google Photosにアップロードというのを自動化する方法を実施しました。
だいぶ快適になりましたが、FlashAirの無線LANは電波の送受信が弱く、WebDAVとしても非力なため、転送が中断されることもしばしばありました。
転送が中断されると、写真の半分がグレーになった写真がアップされてしまいます。
また、中断中はサーバースクリプトが長くダンマリするため、カメラの電源OFF/ONしてFlashAirを復旧しても、Google Photosへのアップロードが始まらないということがあったわけです。
この辺を直してみました。
まず、FlashAirからの転送が中断するとダンマリになる原因は、wgetのデフォルトの振る舞いにありました。
wgetを使ってFlashAirからhttpで写真を転送します。
#!/bin/bash #初期変数一覧 #Flashairのアドレス FLASHAIR_IP="http://192.168.x.161" #DCIMフォルダの名称 DCIM_FOLDER="CANON" #DCIMフォルダのURLを生成 WGET_PRE_URL=${FLASHAIR_IP}"/DCIM/" #(中略) echo "Flashair - Get file "${JPG_LIST[JPG_LOOP]} /usr/bin/wget -q -N --no-host-directories --no-directories ${FLASHAIR_IP}${JPG_LIST[JPG_LOOP]}
このとき、wgetのタイムアウトは900秒、リトライ回数は20回です。
つまり、15分以上ダンマリを決め込むわけです。いくら気長な私でも待てません。
タイムアウトを30秒、リトライを5回にすると、約1分くらいで処理をあきらめます。
#!/bin/bash echo "Flashair - Get file "${JPG_LIST[JPG_LOOP]} /usr/bin/wget -q --tries=5 --timeout=30 --no-host-directories --no-directories ${FLASHAIR_IP}${JPG_LIST[JPG_LOOP]}
ついでに、既存ファイルの上書き禁止の-Nオプションを廃止しました。
さて、続いてこの中断したファイルを消すことと、正しく転送されたファイルリスト「flashair_done.txt」に追記する処理をスキップしなければなりません。
まず、wgetの終了ステイタスコードを理解します。
- 0 No problems occurred.(正常終了)
- 1 Generic error code.(一般的なエラー)
- 2 Parse error—for instance, when parsing command-line options, the ‘.wgetrc’ or ‘.netrc’...(パースエラー、.wgetrcや.netrcの記述が間違っている)
- 3 File I/O error.(ファイル入出力エラー)
- 4 Network failure.(ネットワークの異常)
- 5 SSL verification failure.(SSL認証エラー)
- 6 Username/password authentication failure.(ユーザー/パスワード認証エラー)
- 7 Protocol errors.(プロトコルエラー)
- 8 Server issued an error response. (サーバーがエラーを応答)
FlashAirの異常終了はほぼほぼ4ですが、まあ正常終了の0以外はファイル削除することにします。
シェルスクリプトの中で直前のコマンドの終了ステイタスコードは「$?」に代入されます。しかも、次に何らかのコマンドが実行されると消えてしまいますので、何らかの変数に代入しておきます。
0なら正常終了なので「flashair_done.txt」に追記します。
#!/bin/bash #wgetによるファイルの取得 echo "Flashair - Get file "${JPG_LIST[JPG_LOOP]} /usr/bin/wget -q --tries=5 --timeout=30 --no-host-directories --no-directories ${FLASHAIR_IP}${JPG_LIST[JPG_LOOP]} #wgetのステイタスを確認 WGET_STATUS=$? echo "Flashair - wget exit status is "$WGET_STATUS if [ $WGET_STATUS -eq 0 ]; then #wgetが正常終了ならflashair_done.txtにファイル名を追加 echo "Flashair - Output to flashair_done.txt "${JPG_LIST[JPG_LOOP]} echo ${JPG_LIST[JPG_LOOP]} >> flashair_done.txt
つづいて、else以下にwgetの終了ステイタスコードが0以外、つまりwget異常終了時の振る舞いとして、ダウンロード途中で終わった現在のファイルを削除します。
が、${JPG_LIST[JPG_LOOP]}には、/DCIM/100CANON/1NA0001.JPGのように、ディレクトリ名まで代入されています。
しかし、中途半端にダウンロードしているファイルは、スクリプトが実行されているフォルダの直下に1NA0001.JPGとして保存されています。"/DCIM/100CANON/"は文字列置換で削除する必要があります。
sedを使って削除です。
ちょうど良いことに、${DCIM_LIST[DCIM_LOOP]}にこの"/DCIM/100CANON"(配列になっており、100だけじゃなく101だったり102もあったりします)が代入されていますので、これと/の1文字を削除です。
#!/bin/bash DEL_JPEG=(`echo ${JPG_LIST[JPG_LOOP]} | sed -e "s@${DCIM_LIST[DCIM_LOOP]}@@" -e "s@/@@"`) rm $DEL_JPEG DEL_JPEG=
ずーっとsed -e "s/${DCIM_LIST[DCIM_LOOP]}//"と記載していて、sedシンタックスエラーで悩んでいたのですが、変数に"/"が入っているため、sedがそれを変数としてではなく、セパレーターとして認識してしまっていたため、セパレーターを@にすることで解決しました。
では、リトライを調整して完成した改善後のスクリプト全文はコチラです↓
(192.168.x.161のIPアドレスは、FlashAirのIPアドレスに置き換えてください。)
#!/bin/bash #初期変数一覧 #Flashairのアドレス FLASHAIR_IP="http://192.168.x.161" #DCIMフォルダの名称 DCIM_FOLDER="EOS7D" #現在のディレクトリに移動 cd `dirname $0` #プロセスIDを保存 echo $$ > pid_flashair.txt #DCIMフォルダのURLを生成 WGET_PRE_URL=${FLASHAIR_IP}"/DCIM/" echo "Starting Get Photos from Flashair" while : do #Flashairを死活監視 ALIVE=$(/usr/bin/wget -nv --spider --timeout 60 -t 1 ${FLASHAIR_IP} 2>&1 | grep -c '200 OK') #もし死活監視で生きていたら if [ $ALIVE -eq 1 ]; then #配列を初期化 echo "Flashair - Format Arrys." DCIM_LIST=() JPG_LIST=() FLASHAIR_DONE=() WGET_STATUS= #DCIM配下のディレクトリを配列に代入 echo "Flashair - get under DCIM directory list." DCIM_LIST=(`/usr/bin/wget ${WGET_PRE_URL} -q -O - | grep 'wlansd.push({"r_uri"' | sed -e 's/wlansd.push({"r_uri":"//' -e 's/", "fname":"/\//' -e 's/", "fsize".*//' | grep ${DCIM_FOLDER}`) #フォルダの数だけループ処理を実施 echo "Flashair - Starting loop under DCIM directory." DCIM_LOOP=${#DCIM_LIST[@]} DCIM_LOOP=$((DCIM_LOOP - 1)) while [ $DCIM_LOOP -ge 0 ]; do #フォルダの中のファイルを配列に代入 echo "Flashair - Get file list under "${DCIM_LIST[DCIM_LOOP]} JPG_LIST=(`/usr/bin/wget ${FLASHAIR_IP}${DCIM_LIST[${DCIM_LOOP}]} -q -O - | grep 'wlansd.push({"r_uri"' | sed -e 's/wlansd.push({"r_uri":"//' -e 's/", "fname":"/\//' -e 's/", "fsize".*//'`) #ファイルの数だけループ echo "Flashair - Starting loop under "${DCIM_LIST[DCIM_LOOP]} JPG_LOOP=${#JPG_LIST[@]} JPG_LOOP=$((JPG_LOOP - 1)) while [ $JPG_LOOP -ge 0 ]; do #アップ済みのファイルかどうかをチェック echo "Flashair - Check FLASHAIR_DONE "${JPG_LIST[JPG_LOOP]} if ! `cat flashair_done.txt | grep -q "${JPG_LIST[JPG_LOOP]}"` ; then #アップ済みになかった場合の処理 #wgetによるファイルの取得 echo "Flashair - Get file "${JPG_LIST[JPG_LOOP]} /usr/bin/wget -q --tries=5 --timeout=30 --no-host-directories --no-directories ${FLASHAIR_IP}${JPG_LIST[JPG_LOOP]} #wgetのステイタスを確認 WGET_STATUS=$? echo "Flashair - wget exit status is "$WGET_STATUS if [ $WGET_STATUS -eq 0 ]; then #wgetが正常終了ならflashair_done.txtにファイル名を追加 echo "Flashair - Output to flashair_done.txt "${JPG_LIST[JPG_LOOP]} echo ${JPG_LIST[JPG_LOOP]} >> flashair_done.txt #wgetが異常終了ならダウンロード途中のファイルを削除 else DEL_JPEG=(`echo ${JPG_LIST[JPG_LOOP]} | sed -e "s@${DCIM_LIST[DCIM_LOOP]}@@" -e "s@/@@"`) echo "Flashair - Download Uncomplete. Delete File "$DEL_JPEG rm $DEL_JPEG DEL_JPEG= fi fi JPG_LOOP=$((JPG_LOOP - 1)) done DCIM_LOOP=$((DCIM_LOOP - 1)) #ダウンロードした全てのJPGを2048pxにリサイズ echo "Flashair - Resize Images" /usr/bin/mogrify -resize 2048x2048 -quality 100 *.JPG #ディレクトリ上にあるJPGファイルをpicasaweb APIでアップロード echo "Flashair - Uploading Images" ./upload_images_in_dir.sh #ディレクトリ上にあるJPGファイルを日付ディレクトリで整理 echo "Flashair - Move Images to YMD directory " /usr/bin/exiftool -q '-Directory < CreateDate' -d %Y%m%d *.JPG done fi sleep 10 done
これで、スクリプトの再起動はしばらくやらなくてよさそう。