Nextcloud - Preview Generatorを外部ストレージで使用する方法

2022-10-13
  • B!

Nextcloud TrueNAS

 NextcloudのPreview Generatorを使用すると、プレビュー画像が事前に生成されて画面表示が速くなるという利点がある。これまでも設定方法についてここで書いてきた。

しかし実はPreview Generatorには欠点もある。cronによるバックグラウンド処理により、アップロードされた新規ファイルを見つけてプレビュー画像を生成するアプリなのであるが、外部ストレージ(External Storage)に保存された新規ファイルのスキャンはできないのである。少し具体的に書くと、

全てのファイルをスキャンする
External Storageも処理の対象となる
% php ./occ preview:generate-all 

新規ファイルのプレビューだけを作成
External Storageは処理の対象とならない
% php .occ preview:pre-generate

このように、cronで動かす差分アップデートコマンド(preview:pre-generateが問題となる。これはNextcloud本体に対して3rd partyアプリが使用可能なライブラリの機能の制約から来ているようなのでアプリレイヤでは解決が難しい問題である。

このような理由で外部ストレージのPreview Generatorの利用は諦めてかけていたのだが、私の環境はTrueNASの共有ディレクトリをNextcloudで閲覧する構成で組んでいるので、Nextcloudは外部ストレージをメインのストレージとしているのである。そのため、どうしてもこの問題をなんとか解決したいのである。

この問題は公式のフォーラムチケットでも取り上げられており、同じ悩みを抱えている人は世界中にいるのに根本的な解決に至っていない。そこで、解決策を作ってみた。

解決方法のロジック

Preview Generatorは処理キューのテーブルに登録されたファイルのプレビューを作成する。そこで、この処理キューのテーブルにExternal Storageのファイルも登録する方法で解決させる。

Preview Generatorのキュー管理

少し具体的に説明する。Preview Generatorは review:pre-generate を実行すると、処理キューを管理するテーブル oc_preview_generation に登録されているファイルIDのファイルを見つけ出してプレビュー画像を生成する。 oc_preview_generation にはこのようにデータが格納されている。
$gt; select * from oc_preview_generation;
+----+--------+---------+
| id | uid    | file_id |
+----+--------+---------+
| 10 | uname  |     615 |
| 11 | uname  |     616 |
+----+--------+---------+
2 rows in set (0.00 sec)
最大1000件まで登録される。uidはこのファイルへアクセス権限があるユーザーでかつ、Preview Generatorの利用権限があるユーザーだ。このこのテーブルに外部ストレージのファイルIDを登録すれば良いのだ。idはauto incrementなので次々INSERTすれば良い。テーブルのスキーマは以下。
gt; show columns from oc_preview_generation;
+---------+--------------+------+-----+---------+----------------+
| Field   | Type         | Null | Key | Default | Extra          |
+---------+--------------+------+-----+---------+----------------+
| id      | int          | NO   | PRI | NULL    | auto_increment |
| uid     | varchar(256) | NO   |     | NULL    |                |
| file_id | int          | NO   |     | NULL    |                |
+---------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

file_idの探し方

次の課題は適切な fild_idoc_preview_generation へINSERTすることだ。
ファイルを探す前に、External StorageのIDを探す。ストレージのマウントポイントを管理している oc_storages を取得する。
> select * from oc_storages;
+------------+---------------------------------------+-----------+--------------+
| numeric_id | id                                    | available | last_checked |
+------------+---------------------------------------+-----------+--------------+
|          1 | home::ncadmin                         |         1 |         NULL |
|          2 | local::/usr/local/www/nextcloud/data/ |         1 |         NULL |
|          3 | home::uname                           |         1 |         NULL |
|          4 | local::/media/z_dev_photos/           |         1 |   1642304007 |
+------------+---------------------------------------+-----------+--------------+
4 rows in set (0.00 sec)
外部ストレージは過去にこちらで設定した /media/z_dev_photos/ だ。そのストレージのIDは numeric_id = 4 である。

次にストレージIDが4であるファイルの最新ファイルのIDを探す。
> SELECT MAX(fileid) FROM oc_filecache WHERE storage=4 AND mimetype!=2;
+-------------+
| MAX(fileid) |
+-------------+
|       68418 |
+-------------+
1 row in set (0.06 sec) 
SQL文中の mimetype!=2  とはディレクト以外という意味だ。これで外部の storage id = 4 の最新のファイルのIDが68418であることがわかる。

さて、この後で新しくファイルを3つアップロードしたとする。新しいファイルを探す方法は、以前の最新ファイルID = 68418よりも番号が大きいファイルを探せば良い。
> SELECT fileid, path FROM oc_filecache WHERE storage=4 AND fileid>68415 AND mimetype!=2;
+--------+--------------------------+
| fileid | path                     |
+--------+--------------------------+
|  68416 | 2007年/004/IMGP4976.JPG  |
|  68417 | 2007年/004/IMGP5344.JPG  |
|  68418 | 2007年/004/HI3A0006.JPG  |
+--------+--------------------------+
3 rows in set (0.01 sec)
これらが新たにアップロードされたファイル一覧となる。これらのプレビューが生成されるように、はじめに書いたプレビュー作成キューの oc_preview_generation にこれらのidを登録すれば良いのである。fileid = 68416 を登録するSQLは次のようになる。
> INSERT INTO oc_preview_generation (uid, file_id)
VALUES ('uname', 68416);
Query OK, 1 row affected (2.66 sec)


> select * from oc_preview_generation;
+----+--------+---------+
| id | uid    | file_id |
+----+--------+---------+
| 14 | uname  |   68416 |
+----+--------+---------+
1 row in set (0.00 sec)
あとはこの作業を繰り返して、新規ファイルのID全てを登録すれば良いのである。このあとは、いつもの通り更新ファイル分のプレビュー作成を実行する。
% php .occ preview:pre-generate
これがcronで起動すると、プレビュー画像ファイルが作成される。

実装 - Preview Generator Helper

手作業でMySQLを操作して、外部ストレージをプレビュー作成のキューに登録することで動作ができることが確認できた。しかし、毎度これを手作業で行うわけにもいかないのでスクリプトで処理をすることにする。

ソースコードはGitHubに置いた。

使い方

まずはスクリプトの冒頭の設定を書く。

# Config. to connect MySQL DB
our $DB_NAME = "nextcloud";
our $DB_USER = "dbadmin";
our $DB_PASS = "password";
our $DB_HOST = "localhost";
our $DB_PORT = "3386";
# Nextcloud - to specify a storage to seek
our $NC_USER = "nextuser";  # User name who can read the storage
our $NC_STORAGE_ID = 7;		# Storage ID which is found on oc_storages
# To record the last file_id to seek
our $LAST_ID_FILE = "/usr/local/www/last_file_id.dat";
$DB_*** はMySQLへの接続の設定。 $NC_STORAGE_IDoc_storages テーブルで定義されているストレージのID。 $LAST_ID_FILE は前回チェックした時の最新ファイルIDを記録するファイル名。この情報を使いスクリプトが起動したときに前回の起動の後に新規追加されたファイルIDだけを抽出して、プレビュー作成キュー oc_preview_generation へ登録する。 $NC_USER は oc_preview_generation へINSERTするときに使うuseridである。

設定が完了したら、一度この previewhelper.pl を実行する。その後、$LAST_ID_FILE が生成される。このファイルの中に最後に更新されたファイルのIDが記録される。

2回目以降の起動時には、$LAST_ID_FILE に記録されているファイルIDを確認して、新規にアップロードされたファイルのIDだけを oc_preview_generation へ登録する。

これ以降は、この作業を定期的に繰り返せば良い。cronを使うと良い。

TrueNASのNextcloudプラグインで使用する場合

TrueNASのNextcloudプラグインの環境で実行する場合は、DBD::mysqlモジュールの追加インストールが必要。手順はこちらを参考に。

制約事項

このスクリプトも完璧ではなくて、制約事項がある。それはNextcloudで開いたことがない階層のフォルダに含まれているファイルは検出できないという点。これはNextcloud本体の制約である。 外部ストレージのディレクトリをNextcloudのWeb画面で開くか、NextcloudのWebDAVで開くかしないとそのフォルダ内のファイルが検出されない。

外部ストレージの場合はNextcloudの外でストレージが管理されているので、Nextcloudが知らないタイミングでファイルやフォルダが追加されることがある。これを検出するのは難しいようだ。もしも外部ストレージのファイル更新を全て自動で検出できるようにしたいならば、NextcloudのWebDAVで外部ストレージのディレクトリを探索するスクリプトを書くのが良いだろう。