Pine A64で写真振り分けシステムを作ってみた。

過去に写真の管理を出来るだけ自動でやりたいというエントリを書いております。
写真の管理をできるだけ自動でやりたい(flickr uploader編)
写真の管理をできるだけ自動でやりたい(exiftool編)
ReadyNASでAmazonプライムフォトに写真データをアップする

しかし、いままでのシステムはすべてパソコンで何かをする必要があったのです。
パソコンを立ち上げて、それから処理をしないといけない。面倒です。常にスリープ状態でPCを運用していますが、それでも面倒。
出来ることなら、カード挿して終わりというシステムがいい。

PineA64があれば出来るんじゃないかと思い立ち、実行してみました。

目指すシステム

  • SDカードを挿す
  • マウントされる
  • マウントされたら処理が始まる
    • NASをマウントする
    • ファイルを振り分けながらコピーする
  • 処理が終わったらアンマウントする

設計は以上です。

スクリプトは以前に作ったものを流用

以前に作った写真振り分けスクリプトを流用します。
写真の管理をできるだけ自動でやりたい(exiftool編)

ただpythonのスクリプトなので、単純なコマンド実行(処理が終わったらアンマウントするなど)が面倒なので、シェルスクリプトを用意してそのあたりの処理をし、そこから写真振り分けスクリプトを呼び出します。

コピー先のNASはautomountで

常にNASをマウントしておくと、NASのメンテなどで再起動する場合にトラブルの元になるので、使うときだけマウントします。
automauntを使えばそれが実現可能です。

参考:Windows の認証付き共有フォルダを Ubuntu で自動マウント – Qiita

これも参考先と同様に作業を行います。

/etc/auto.masterに以下の行を追加します。


/smb /etc/auto.smb

/etc/auto.smbには以下の行を追加


opts="-fstype=cifs,iocharset=utf8,rw,noperm,uid=root,gid=root,credentials=/root/readynas.cnf"

認証情報を記載したreadynas.cnfを用意


username=USERNAME
password=PASSWORD
domain=VOLUME

これで準備は完了です。あとはマウント先の名前解決が出来るようにhostsファイルに記載しておけばオッケーです。

テストしてみます。


◯/smb以下に何も無い事を確認
root@pine64:~# ls /smb/
◯/smb/readynas(自動マウントするNASのホスト名)が存在するかを確認
root@pine64:~# ls /smb/readynas
AmazonCloudDrive  Documents  Music  Pictures  Videos
◯/smb以下にreadynasが存在している。
root@pine64:~# ls /smb
readynas

これで動作は問題なさそうです。しばらくアクセスがなければアンマウントされます。

SDカードの自動マウントはudevで

SDカードを挿すと自動マウントされるという処理が必要です。自動マウントなので、NASと同様にautomountかなと思ったのですが、この場合はudevがよさそうです。
今回のシステムはシンプルにカードしか挿さないという前提なので、そこまで難しく考えずにやってみます。

参考にしたのはこのサイト
参考: udevのruleファイルについて KazHatブログ/ウェブリブログ
スクリプトはほぼ上記サイトのコピペですが、ちょっとだけカスタマイズした感じです。

/etc/udev/rules.d/以下に、ファイルを作成します。

/etc/udev/rules.d/11-media-automount.rules


KERNEL!="sd[a-z][0-9]", GOTO="sd_cards_auto_mount_end"
# Global mount options
ACTION=="add", ENV{mount_options}="relatime"
# Filesystem specific options
ACTION=="add", IMPORT{program}="/sbin/blkid -o udev -p %N"
ACTION=="add", ENV{ID_FS_TYPE}=="vfat|ntfs", ENV{mount_options}="$env{mount_options},utf8,gid=100,umask=002"
ACTION=="add", RUN+="/bin/mkdir -p /media/sd-%k", RUN+="/bin/mount -o $env{mount_options} /dev/%k /media/sd-%k"
# exit
LABEL="sd_cards_auto_mount_end"

参考元のスクリプトだと、アンマウントの際に/media以下に作製したマウントポイントのフォルダを削除してます。
そこを実行する際にエラーになったので原因追及せずに削除しています。まあマウントポイントが残るだけですからね。
あと自動マウント対象のデバイスがsdaからsdzになっています。通常はsdaはすでにブート用のディスクなどで使われていますが、Pine A64の場合にはブートデバイスのデバイス名がsdaではなかったのと、SDカードリーダをいくつか試すと、sdaを使うパターンがあったために修正しています。

udevでスクリプトを実行、しかし

スクリプトを配置したので、あとは自動的にスクリプトが実行されるだけです。
udevのルールファイルにスクリプト実行(RUN+=”/bin/bash /usr/local/bin/photo_move”)を追記します。photo_moveは処理に使用するスクリプトの名前です。


KERNEL!="sd[a-z][0-9]", GOTO="sd_cards_auto_mount_end"
# Global mount options
ACTION=="add", ENV{mount_options}="relatime"
# Filesystem specific options
ACTION=="add", IMPORT{program}="/sbin/blkid -o udev -p %N"
ACTION=="add", ENV{ID_FS_TYPE}=="vfat|ntfs", ENV{mount_options}="$env{mount_options},utf8,gid=100,umask=002"
ACTION=="add", RUN+="/bin/mkdir -p /media/sd-%k", RUN+="/bin/mount -o $env{mount_options} /dev/%k /media/sd-%k" , RUN+="/bin/bash /usr/local/bin/photo_move"
# exit
LABEL="sd_cards_auto_mount_end"

これでSDカードを挿すとスクリプトが実行されます。これで完了したと思ったのですが・・・
どうもスクリプト実行時にタイムアウトが発生して失敗するのです。いろいろ調べて見たところudevだと処理のタイムアウト時間が短いようです。
そういう場合には、systemdでサービス化して実行するのが良さそうです。

スクリプトはsystemdでサービス化

systemdを使えばスクリプトがサービス化可能で、サービスにしてしまえばudevのタイムアウト制限に引っかからないという仕組みです。
参照:Systemdを使ってさくっと自作コマンドをサービス化してみる – Qiita

参照サイトを参考に作製したのが以下のUnitファイル。これを/etc/systemd/system以下に配置します。


[Unit]
Description = photo move daemon

[Service]
ExecStart = /usr/local/bin/photo_move
Type = oneshot

[Install]
WantedBy = multi-user.target

これでsystemctlつかって操作できるはずです。試してみます。


root@pine64:~# systemctl status photo-move.service
● photo-move.service - photo move daemon
   Loaded: loaded (/etc/systemd/system/photo-move.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

Apr 29 17:14:02 pine64 systemd[1]: Starting photo move daemon...
Apr 29 17:14:02 pine64 [5987]: photo move is start
Apr 29 17:14:03 pine64 photo_move[5986]: DATAPATH is not found
Apr 29 17:14:03 pine64 systemd[1]: Started photo move daemon.
Apr 29 17:19:44 pine64 systemd[1]: Starting photo move daemon...
Apr 29 17:19:44 pine64 [6971]: photo move is start
Apr 29 17:27:29 pine64 systemd[1]: Started photo move daemon.
Apr 29 17:35:28 pine64 systemd[1]: Starting photo move daemon...
Apr 29 17:50:45 pine64 [18194]: unmounted /media/sd-sda1
Apr 29 17:50:45 pine64 systemd[1]: Started photo move daemon.

こんな感じで。

udevのルールファイルは以下のようになりました。


KERNEL!="sd[a-z][0-9]", GOTO="sd_cards_auto_mount_end"
# Global mount options
ACTION=="add", ENV{mount_options}="relatime"
# Filesystem specific options
ACTION=="add", IMPORT{program}="/sbin/blkid -o udev -p %N"
ACTION=="add", ENV{ID_FS_TYPE}=="vfat|ntfs", ENV{mount_options}="$env{mount_options},utf8,gid=100,umask=002"
ACTION=="add", RUN+="/bin/mkdir -p /media/sd-%k", RUN+="/bin/mount -o $env{mount_options} /dev/%k /media/sd-%k"
ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}+="photo-move.service"
# exit
LABEL="sd_cards_auto_mount_end"

“ACTION==”add”, TAG+=”systemd”, ENV{SYSTEMD_WANTS}+=”photo-move.service””
この箇所がsystemdのUnitを呼び出す記述になります。これで基本的な動作に関しての設定は完了です。

処理中はLEDを点灯させよう

ここまでで動作的には問題無くなりました。
しかしPineA64にはモニタをつなげていないので、処理が終わってもわかりません。

そこでスクリプトの処理に合わせて、スクリプトの点灯と消灯を行うことにしました。
参照:PINE64のGPIOでLEDを点灯させる | エンジニアの日常

LEDの配線に関してはブレッドボードを使いました。お試しなのでAmazonで必要十分そうなパーツをチョイスしました。

詳細については参照元を見ていただくとして・・・
最終的に完成したスクリプトはこんな感じになりました。


#!/bin/bash
GPIO_PORT=/sys/class/gpio/gpio71
GPIO_VALUE=$GPIO_PORT/value

logger -t [photo_move] photo move is start

echo 1 > $GPIO_VALUE

/usr/local/bin/photo-selector

if [ $? = 0 ]; then
  logger -t [photo_move] photo move is end
  SDCARD=`find /media -name DCIM | cut -d / -f 1-3`
  if [ -n "$SDCARD" ]; then
    umount $SDCARD
    logger -t [photo_move] unmounted $SDCARD
  fi
else
  logger -t [photo_move] photo move is error
fi

echo 0 > $GPIO_VALUE

このスクリプトでやってるのは、メディアのアンマウントとログへの書き込み、あとはLEDの点灯と消灯です。
LEDはGPIOポートに割り当てられているvalueに1を書き込むと点灯、0を書き込むと消灯します。

とりあえず完成

とりあえずできあがりです。
まあ改良点は多々ありますが、自宅で家族だけ使うっていうレベルには達したかなと思っております。
処理速度の問題等々を改良しつつ運用していく予定です。