Go 言語の習作に watchevent ってファイルシステム監視するやつ作った

習作って言っても以前 Go 触ってたことはありましたが、 そのツールを作ったこと自体忘れてたぐらい記憶が抜け落ちていたので、改めて Go に再入門しました。

作ったもの

使い方は README.md 読んでください(まだ不十分だと思いますが、分からない点は issue に上げてくれると助かります…)。

github.com

各種バイナリは GitHub release のページにあります(watchevent コマンドしか入ってません)。

きっかけ

自分は Subsonic というストリーミングサーバーを自宅に立てて運用してますが、 いくつか不満がありました。

  1. flac ファイル特有の現象なのかは分からないが)flac ファイルを再生すると曲が終わっても次の曲に行ってくれない
    • 曲の時間が明らかに長かったりするので、終わりが正しく取得できてない?
  2. 音楽フォルダのスキャンが一日一回に限られている
    • 曲が追加されたら一定時間後に自動的にスキャンしてほしい
  3. 最近のバージョンのコードはクローズド(version 6.0-beta1 以降)

Subsonic に不満があったので OSS 版の Libresonic をインストールしてみた - Humanity

ちなみに上記記事では同じくストリーミングサーバの Libresonic に移行したと書いてるのですが、色々問題点があって結局戻ってきました。 それについては気が向いたら詳しく別記事で書きますが、なんと言っても第一の理由は今回利用する API が Libresonic では対応してなかったからです。 それが音楽フォルダのスキャンを開始する API です。

Subsonic では音楽フォルダに mp3 ファイルをポンと置いてもすぐ Web に現れる訳ではなく、 音楽フォルダのスキャンを行う必要があります。 しかしこのスキャンのタイミングがデフォルトでは夜の3時のみで、不便なことに定期スキャンは1日1回だけという制限があります(時間は変えられます)。

もちろんこれでは不便すぎるので Subsonic にはアップロード機能があって、 アップロードした音楽ファイルであれば即座に認識されます。 しかしアップロードするために ZIP で固めるのが面倒だったり、 何となくディレクトリ構造は手動で決めたいという欲求から自分はアップロード機能は使ってません。

しかしそれでは手元にある音楽ファイルを音楽フォルダに移動してすぐ聴きたいという時に不便です。 幸いスキャンは設定画面から手動でも行うことができるので、音楽フォルダに移動するたびにわざわざスキャンのボタンをポチっていましたが、できれば自動化したい。

そこで SubsonicREST API のページを眺めていると前述のスキャンを行うエンドポイントがあると書いてありました。 ならファイルの変更を検知して curl で叩くツール作ればいいのでは?ということで作りました。

Systemd で管理する

自分は watchevent は Systemd で管理して使ってますので、 ついでにそのインストール手順を書きます。

まずバイナリを GitHub release のページからダウンロードしてきて、 どこでもいいですが /opt/watchevent に置きます。

$ # ダウンロード URL は OS, arch によって変えてください
$ curl -LO https://github.com/tyru/go-watchevent/releases/download/v0.0.1/watchevent-v0.0.1-linux-amd64.tar.gz
$ tar xzf watchevent-v0.0.1-linux-amd64.tar.gz
$ sudo mkdir /opt/watchevent/
$ sudo install -o root -g root -m 0755 watchevent-v0.0.1-linux-amd64/watchevent /opt/watchevent/watchevent

次に各設定ファイルを作ります。

/etc/systemd/system/watchevent.service

[Unit]
Description=Watchevent

[Service]
Type=simple
EnvironmentFile=-/etc/default/watchevent
ExecStart=/opt/watchevent/watchevent -c /etc/watchevent/watchevent.yml $DIRECTORY
PrivateTmp=true

[Install]
WantedBy=multi-user.target

/etc/default/watchevent

DIRECTORY="-d {監視対象のディレクトリ}"

ちなみにこんな風に2個以上監視することもできます。

DIRECTORY="-d dir1 -d dir2"

/etc/watchevent/watchevent.yml

action:
  - name: post-subsonic-rescan
    on: [all]
    # 30s after latest file change, access to Subsonic API
    interval: 30s
    interval_action:
      - on: [all]
        do: cancel
    run: curl 'http://localhost:4040/rest/startScan?u=user&t=token&s=salt&v=1.15.0&c=watchevent-curl'

log:
  level: "info"
  encoding: "console"
  #encoding: "json"
  encoderConfig:
    messageKey: "msg"
    levelKey: "level"
    timeKey: "time"
    nameKey: "name"
    callerKey: "caller"
    stacktraceKey: "stacktrace"
    levelEncoder: "capital"
    timeEncoder: "iso8601"
    durationEncoder: "string"
    callerEncoder: "short"
  outputPaths:
    - "stdout"
  errorOutputPaths:
    - "stderr"

ちなみに Subsonic API の解説もしておくと、curl コマンドの引数の URL の token はこんな風に求めます。

$ echo -n '{パスワード}{適当に決めたsalt}' | md5sum 

で出力された MD5 値を token に指定します。 うまくいかなければ u (username), p (password), c (client) パラメータだけでもいけます(がセキュリティ上よろしくない)。

上記設定ファイルを作った後、

$ sudo systemctl daemon-reload
$ sudo systemctl enable watchevent
$ sudo systemctl start watchevent

として起動します。 ちゃんと動作してるかログが見たい方は

$ journalctl -ef -u watchevent

とかすると watchevent の出力が tail -f されるので眺めるといいかもしれません。