🦜

Go製ツールでGoogleフォトクローンを立ち上げる

2024/09/17に公開
8

ふとGoogleフォトの残容量をみると2%を切りました。これがゼロ%になるとどうなるか通知が来ていました。

空き容量がなくなると、写真のバックアップや Gmail でのメールの送受信ができなくなります。

こ、怖い。GMailまで送受信できなくなっちゃうの!?
これは早急に対策を考えねば。

現状の分析

  • 200GBのGoogleドライブの契約済み(年額3800円)
  • Googleフォト自体にほぼ不満なし(唯一あるのは同期済み削除が素人泣かせなところ)
  • 十数年分の持ち込み+約9年でフォトに190GB、メール&ドライブに8GB、残容量2%表示で残り3.8GB
  • もうひとつ上の契約は2TB(年額13000円)
  • 500GBもあれば10年以上は大丈夫な消費ペース
  • 2TBはオーバースペックすぎる
  • 500GBで5~6000円のプランがあれば、迷わず契約するところなんだけど・・・。

取れる手法

  • ストレージをひっ迫するデータを削除(お気に入りに入れてお気に入り以外を削除)
  • 年額13000円払ってGoogleフォトを利用継続
  • Amazonフォトに写真だけでも退避
  • 自前でNASを買って自宅に設置
  • 自前でNASを作って自宅に設置

いろいろ悩みましたが最後の案を採用しました

理由

  • 簡単に削除しても良いと判断できるものは意外と少なく、削除してもしばらくしたら同じ状況になりそう
  • 13000円を何年も払う=高級NASが買える
  • Amazonに逃がしてもフォトと関連動画が同じ時系列に並んでることが自分には重要だった
  • NASを買う場合、SSD内包NASはほぼ存在しない
  • そうなるとバックアップとか閲覧とかがどうしても鈍重になる
  • SSDをバラ買いしてNASを組むと結構高価になる
  • 自作がお手頃で自分の満足できる保管フローが作れる

LAN内サービスを公開する戦略

  • DDNS+DMZ
  • Ngrok有料サービス(URL固定の場合、月額10USD)
  • Tailscale Funnel
  • CloudFlare Tunnel(サーバー証明書と独自ドメイン必要)

DDNS+DMZは安定しないのと、自宅ネットワークを危険にさらすことにならないよう注意深くメンテする必要があります。たまたま独自ドメインとCloudFlareでLetsEncryptを使ったサーバー証明書を運用していたので「CloudFlare Tunnel」を採用しました。

計画の立案

  • RaspberryPi4+2TBストレージ(NAS)を自宅LAN内に設置
  • Go製の「PhotoPrism」で写真や動画を管理
  • CloudFlareのトンネルサービスでLAN内のPhotoPrismサービスにつなげられるようにする
  • Go製の「Syncthing」でバックアップ

大まかな構成図

スマホの写真吸い上げ

  • これまでも家に帰ってWi-Fi接続時のみGoogleフォトに送るようにしていた
  • 家に帰ってWi-Fiにつなげたときだけにスマホ内の写真をPhotoPrismに送れれば良い
  • 以下のアプリなら2タップで全写真を同期し、同期完了したものを削除までできる
  • アプリ内広告の除去に400円程度は必要
  • Googleフォトは同期を大まかに自動で行ってくれるが、削除の手順は煩雑だった
  • 以下のアプリのほうが明示的な手順は必要だけど同期から削除までを一気に実行してくれる

サードパーティ製

こちらは400円で広告なしにでき、アップロードと削除だけならこれで十分。

https://apps.apple.com/jp/app/photo-uploader-for-photoprism/id1607500083
https://play.google.com/store/apps/details?id=com.smka.prismuploader&hl=ja

純正はこちら

https://www.photoprism.app/partners

  • 純正は年額・月額・払い切りが選べる(プレミアム払い切り3500円)
  • プレミアムの場合バックグラウンド自動同期機能が使える
  • いろいろ機能が豊富だが、3500円を家族分買うのは重い(ファミリーで共有できるという情報もある)

スマホでの閲覧

  • PhotoPrismサービス自体がPWAになっていて快適な閲覧が可能
  • RaspberryPiとストレージの性能依存ではあるが、家族以外のアクセスをする予定もないので十分

データロスト対策

  • SyncthingによりデスクトップPCとの同期をやっておく
  • バックアップについては機器そのものの多重化で行う
  • 多重化完了時にcloudflaredトンネルの接続を切り替えれば移行できる
  • 数年おきに別機器の同期を増やし、古いほうをそのうち停止する
  • 以上により常に2~3台のミラーリング状態が維持される

Googleフォトとの比較

  • AI機能がまだ発展途上、タグ付け分類のワードが明らかに少ない&英語のみ
  • 省スペース向けの圧縮処理機能がまだないため、今後の消費ペースが倍増する予定
  • 閲覧用(PWA)はアップロードや削除の自動化ができないため、同期用のアプリが別にある
  • 「何年前の今日」などの思い出の表示やその場での画像編集などがない。

実際のセットアップ

AlpineLinux

AlpineLinuxにおけるディスクレスモードイメージをRaspberryPi用に作成します。

ディスクレスモードの説明
  • メインパーティションはリードオンリー
  • tmpfs(オンメモリファイルシステム)や永続化保存のアーカイブがオーバーレイとしてメインパーティションに重ねてマウントされている
  • ファイルを読むときはtmpfs->アーカイブ->メインパーティションの順番に探して見つかったファイルを読む
  • ファイルを作成したり書き込んだりするとtmpfsに書き込まれる
  • コミット操作によりtmpfs上にある変更はアーカイブに保存される
  • そうしていないままリブートしちゃうと、tmpfsにある変更はロストします
  • ただこのおかげでストレージに書き込むタイミングは「コミット」の時のみ
  • メモリカードの寿命へのダメージは最小限にとどめることができます

[VERSION]にはAlpineLinuxのバージョンを指定します。(現在は3.20.3
[SSID]/[PASSPHRASE]には無線LANの接続情報を指定します。
[HOSTNAME]にはLANアクセス用のホスト名を指定します。
fat32でマイクロSDカードをフォーマット。PCに接続します。
[TARGET]にはそのドライブ名D:(Windows)またはマウントボリュームパス(macOSまたはLinux)を指定します。

その上で以下のシェルコマンドを実行します(Goとcurlが必要)。

> curl https://github.com/[GitHubID].keys -o keys
> go run github.com/SWITCHSCIENCE/rpi-alpine-installer@latest -version=[VERSION] -arch=aarch64 -ssid=[SSID] -passphrase=[PASSPHRASE] -authorized_keys=keys -dist=[TARGET] -hostname=[HOSTNAME]

こうして書き込んだマイクロSDカードをPCからアンマウントしてRaspberryPiに差し込んでRaspberryPiを起動すると、初回は1~2分かかりますがその後Wi-Fi接続してLANからアクセス可能になるはず。

以下の接続でRaspberryPiのSSH接続ができます。

ssh root@[HOSTNAME].local

apkリポジトリ設定

SSH経由で以下のコマンドを実行しプロンプトにて
Communityを追加して1番リポジトリを参照するように設定する

> setup-apkrepos
> c
> 1

mdevセットアップ

これをやることでUSBデバイスを自動認識します。

setup-devd mdev

必要なパッケージをインストール

apk add -U go docker curl syncthing

変更の永続化

AlpineLinuxのディスクレスモードではファイルシステムへの書き込みはオンメモリのみ。
それを保存する手続きを取らないと再起動したときにさっぱり忘れてしまいます。

再起動の前に以下のコマンドで作成したファイルの追加とコミット操作をお忘れなく。

> lbu add ###/####
> lbu commit

フォルダ構成

/mnt/storage: ドキュメントルート(外部ハードディスクマウントポイント)

  • /Pictures/: PhotoPrismフォトデータルート
  • /docker/: Dockerルート
  • /database/: MariaDBルート
  • /storage/: PhotoPrismストレージルート

起動時マウント

外部ハードディスクをUSB3につないだ場合、例として/dev/sdaとして認識するのでここをfdiskなどでLinux用パーティションを作成しておき、mkfs.ext4 /dev/sda1としてEXT4でフォーマットしておきます。

外部ハードディスクを/mnt/storageにマウントするためにあらかじめUUIDを調べます。

# blkid
/media/mmcblk0p1/boot/modloop-rpi: BLOCK_SIZE="131072" TYPE="squashfs"
/dev/mmcblk0p1: LABEL="PHOTOS" UUID="????-????" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="????????-??"
/dev/loop0: BLOCK_SIZE="131072" TYPE="squashfs"
/dev/sda1: LABEL="storage" UUID="????????-????-????-????-????????????" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="????????-????-????-????-????????????"

/etc/fstabに以下の行を追記

UUID=外部ハードディスクのUUID       /mnt/storage    ext4    defaults 0 0

Docker

以下のファイルを追加してdockerのストレージを外部ハードディスクの配下にする
/etc/docker/daemon.json

{
  "data-root": "/mnt/storage/docker"
}

PhotoPrism

以下はPhotoPrismの初期docker-compose.ymlに三か所だけ修正を加えたもの。
Pictures, Import, database, storageの4か所を外部ハードディスク配下に設定した。
ラズパイの場合、PHOTOPRISM_WORKERS=2くらいにしておく方が安定するらしいです。
もちろん、パスワード関連は起動を確認後に指定しなおしましょう。

docker-compose.yml
# Example Docker Compose config file for PhotoPrism (Linux / AMD64)
#
# Note:
# - Running PhotoPrism on a server with less than 4 GB of swap space or setting a memory/swap limit can cause unexpected
#   restarts ("crashes"), for example, when the indexer temporarily needs more memory to process large files.
# - If you install PhotoPrism on a public server outside your home network, please always run it behind a secure
#   HTTPS reverse proxy such as Traefik or Caddy. Your files and passwords will otherwise be transmitted
#   in clear text and can be intercepted by anyone, including your provider, hackers, and governments:
#   https://docs.photoprism.app/getting-started/proxies/traefik/
#
# Setup Guides:
# - https://docs.photoprism.app/getting-started/docker-compose/
# - https://docs.photoprism.app/getting-started/raspberry-pi/
# - https://www.photoprism.app/kb/activation
#
# Troubleshooting Checklists:
# - https://docs.photoprism.app/getting-started/troubleshooting/
# - https://docs.photoprism.app/getting-started/troubleshooting/docker/
# - https://docs.photoprism.app/getting-started/troubleshooting/mariadb/
#
# CLI Commands:
# - https://docs.photoprism.app/getting-started/docker-compose/#command-line-interface
#
# All commands may have to be prefixed with "sudo" when not running as root.
# This will point the home directory shortcut ~ to /root in volume mounts.

services:
  photoprism:
    ## Use photoprism/photoprism:preview for testing preview builds:
    image: photoprism/photoprism:latest
    ## Don't enable automatic restarts until PhotoPrism has been properly configured and tested!
    ## If the service gets stuck in a restart loop, this points to a memory, filesystem, network, or database issue:
    ## https://docs.photoprism.app/getting-started/troubleshooting/#fatal-server-errors
    # restart: unless-stopped
    stop_grace_period: 10s
    depends_on:
      - mariadb
    security_opt:
      - seccomp:unconfined
      - apparmor:unconfined
    ## Server port mapping in the format "Host:Container". To use a different port, change the host port on
    ## the left-hand side and keep the container port, e.g. "80:2342" (for HTTP) or "443:2342 (for HTTPS):
    ports:
      - "2342:2342"
    ## Before you start the service, please check the following config options (and change them as needed):
    ## https://docs.photoprism.app/getting-started/config-options/
    environment:
      PHOTOPRISM_ADMIN_USER: "admin"                 # admin login username
      PHOTOPRISM_ADMIN_PASSWORD: "insecure"          # initial admin password (8-72 characters)
      PHOTOPRISM_AUTH_MODE: "password"               # authentication mode (public, password)
      PHOTOPRISM_SITE_URL: "http://localhost:2342/"  # server URL in the format "http(s)://domain.name(:port)/(path)"
      PHOTOPRISM_DISABLE_TLS: "false"                # disables HTTPS/TLS even if the site URL starts with https:// and a certificate is available
      PHOTOPRISM_DEFAULT_TLS: "true"                 # defaults to a self-signed HTTPS/TLS certificate if no other certificate is available
      PHOTOPRISM_ORIGINALS_LIMIT: 5000               # file size limit for originals in MB (increase for high-res video)
      PHOTOPRISM_WORKERS: 2                          # limits the number of indexing workers to reduce system load
      PHOTOPRISM_HTTP_COMPRESSION: "gzip"            # improves transfer speed and bandwidth utilization (none or gzip)
      PHOTOPRISM_LOG_LEVEL: "info"                   # log level: trace, debug, info, warning, error, fatal, or panic
      PHOTOPRISM_READONLY: "false"                   # do not modify originals directory (reduced functionality)
      PHOTOPRISM_EXPERIMENTAL: "false"               # enables experimental features
      PHOTOPRISM_DISABLE_CHOWN: "false"              # disables updating storage permissions via chmod and chown on startup
      PHOTOPRISM_DISABLE_WEBDAV: "false"             # disables built-in WebDAV server
      PHOTOPRISM_DISABLE_SETTINGS: "false"           # disables settings UI and API
      PHOTOPRISM_DISABLE_TENSORFLOW: "false"         # disables all features depending on TensorFlow
      PHOTOPRISM_DISABLE_FACES: "false"              # disables face detection and recognition (requires TensorFlow)
      PHOTOPRISM_DISABLE_CLASSIFICATION: "false"     # disables image classification (requires TensorFlow)
      PHOTOPRISM_DISABLE_VECTORS: "false"            # disables vector graphics support
      PHOTOPRISM_DISABLE_RAW: "false"                # disables indexing and conversion of RAW images
      PHOTOPRISM_RAW_PRESETS: "false"                # enables applying user presets when converting RAW images (reduces performance)
      PHOTOPRISM_SIDECAR_YAML: "true"                # creates YAML sidecar files to back up picture metadata
      PHOTOPRISM_BACKUP_ALBUMS: "true"               # creates YAML files to back up album metadata
      PHOTOPRISM_BACKUP_DATABASE: "true"             # creates regular backups based on the configured schedule
      PHOTOPRISM_BACKUP_SCHEDULE: "daily"            # backup SCHEDULE in cron format (e.g. "0 12 * * *" for daily at noon) or at a random time (daily, weekly)
      PHOTOPRISM_INDEX_SCHEDULE: ""                  # indexing SCHEDULE in cron format (e.g. "@every 3h" for every 3 hours; "" to disable)
      PHOTOPRISM_AUTO_INDEX: 300                     # delay before automatically indexing files in SECONDS when uploading via WebDAV (-1 to disable)
      PHOTOPRISM_AUTO_IMPORT: -1                     # delay before automatically importing files in SECONDS when uploading via WebDAV (-1 to disable)
      PHOTOPRISM_DETECT_NSFW: "false"                # automatically flags photos as private that MAY be offensive (requires TensorFlow)
      PHOTOPRISM_UPLOAD_NSFW: "true"                 # allows uploads that MAY be offensive (no effect without TensorFlow)
      # PHOTOPRISM_DATABASE_DRIVER: "sqlite"         # SQLite is an embedded database that does not require a separate database server
      PHOTOPRISM_DATABASE_DRIVER: "mysql"            # MariaDB 10.5.12+ (MySQL successor) offers significantly better performance compared to SQLite
      PHOTOPRISM_DATABASE_SERVER: "mariadb:3306"     # MariaDB database server (hostname:port)
      PHOTOPRISM_DATABASE_NAME: "photoprism"         # MariaDB database schema name
      PHOTOPRISM_DATABASE_USER: "photoprism"         # MariaDB database user name
      PHOTOPRISM_DATABASE_PASSWORD: "insecure"       # MariaDB database user password
      PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
      PHOTOPRISM_SITE_DESCRIPTION: ""                # meta site description
      PHOTOPRISM_SITE_AUTHOR: ""                     # meta site author
      ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
      # PHOTOPRISM_FFMPEG_ENCODER: "software"        # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi)
      # PHOTOPRISM_FFMPEG_SIZE: "1920"               # video size limit in pixels (720-7680) (default: 3840)
      # PHOTOPRISM_FFMPEG_BITRATE: "32"              # video bitrate limit in Mbit/s (default: 50)
      ## Run/install on first startup (options: update https gpu ffmpeg tensorflow davfs clitools clean):
      # PHOTOPRISM_INIT: "https gpu tensorflow"
      ## Run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, and 900-1200):
      # PHOTOPRISM_UID: 1000
      # PHOTOPRISM_GID: 1000
      # PHOTOPRISM_UMASK: 0000
    ## Start as non-root user before initialization (supported: 0, 33, 50-99, 500-600, and 900-1200):
    # user: "1000:1000"
    ## Share hardware devices with FFmpeg and TensorFlow (optional):
    # devices:
    #  - "/dev/dri:/dev/dri"                         # Intel QSV
    #  - "/dev/nvidia0:/dev/nvidia0"                 # Nvidia CUDA
    #  - "/dev/nvidiactl:/dev/nvidiactl"
    #  - "/dev/nvidia-modeset:/dev/nvidia-modeset"
    #  - "/dev/nvidia-nvswitchctl:/dev/nvidia-nvswitchctl"
    #  - "/dev/nvidia-uvm:/dev/nvidia-uvm"
    #  - "/dev/nvidia-uvm-tools:/dev/nvidia-uvm-tools"
    #  - "/dev/video11:/dev/video11"                 # Video4Linux Video Encode Device (h264_v4l2m2m)
    working_dir: "/photoprism" # do not change or remove
    ## Storage Folders: "~" is a shortcut for your home directory, "." for the current directory
    volumes:
      # "/host/folder:/photoprism/folder"                # Example
      - "/mnt/storage/Pictures:/photoprism/originals"               # Original media files (DO NOT REMOVE)
      # - "/example/family:/photoprism/originals/family" # *Additional* media folders can be mounted like this
      # - "~/Import:/photoprism/import"                  # *Optional* base folder from which files can be imported to originals
      - "/mnt/storage/storage:/photoprism/storage"                  # *Writable* storage folder for cache, database, and sidecar files (DO NOT REMOVE)

  ## MariaDB Database Server (recommended)
  ## see https://docs.photoprism.app/getting-started/faq/#should-i-use-sqlite-mariadb-or-mysql
  mariadb:
    image: mariadb:11
    ## If MariaDB gets stuck in a restart loop, this points to a memory or filesystem issue:
    ## https://docs.photoprism.app/getting-started/troubleshooting/#fatal-server-errors
    restart: unless-stopped
    stop_grace_period: 5s
    security_opt: # see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239
      - seccomp:unconfined
      - apparmor:unconfined
    command: --innodb-buffer-pool-size=512M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
    ## Never store database files on an unreliable device such as a USB flash drive, an SD card, or a shared network folder:
    volumes:
      - "/mnt/storage/database:/var/lib/mysql" # DO NOT REMOVE
    environment:
      MARIADB_AUTO_UPGRADE: "1"
      MARIADB_INITDB_SKIP_TZINFO: "1"
      MARIADB_DATABASE: "photoprism"
      MARIADB_USER: "photoprism"
      MARIADB_PASSWORD: "insecure"
      MARIADB_ROOT_PASSWORD: "insecure"

  ## Watchtower upgrades services automatically (optional)
  ## see https://docs.photoprism.app/getting-started/updates/#watchtower
  ## activate via "COMPOSE_PROFILES=update docker compose up -d"
  watchtower:
    restart: unless-stopped
    image: containrrr/watchtower
    profiles: ["update"]
    environment:
      WATCHTOWER_CLEANUP: "true"
      WATCHTOWER_POLL_INTERVAL: 7200 # checks for updates every two hours
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "/root/.docker/config.json:/config.json" # optional, for authentication if you have a Docker Hub account

起動用スクリプト
/etc/init.d/photoprism

/etc/init.d/photoprism
#!/sbin/openrc-run
name="$RC_SVCNAME"
description="$name docker-compose project"

depend() {
        need net
        need docker
}

start() {
        /usr/bin/docker compose --project-directory "/root" up -d
}

status() {
        /usr/bin/docker compose --project-directory "/root" ps
}

stop() {
        /usr/bin/docker compose --project-directory "/root" down
}

起動と自動起動設定

> rc-service photoprism start
> rc-update add photoprism

Googleフォトの引き込み

正攻法

  • https://takeout.google.com にアクセスしてGoogleフォトのみダウンロードを要求しておきます
  • 1~2日後にメールでダウンロードリンクが届きましたが2GBずつをその数449個!!!
  • 190GBのフォトデータは正攻法のダウンロードではZIP圧縮で合計1TB弱。
  • もしかすると共有で他人の容量消費分もダウンロードされることでサイズが膨らんでいる?
  • 解凍するスペースも含めるとなかなか環境を選ぶ
  • しかも2GBずつの分割で手操作でのダウンロードを要求され、これらの操作はとてもじゃないがやってられない

そこでダウンロードツールを採用

以下のツールはAPI経由で全フォトデータのダウンロードを行うユーティリティ。

https://github.com/dtylman/gitmoo-goog

  • Google CloudにてPhotos Library APIを有効にしたプロジェクトを確保
  • OAuth認証情報をデスクトップ用で作成し、JSONファイルをダウンロード
  • 写真動画を展開したいフォルダ(今回は/mnt/storage/Import)に移動
  • 先ほどのJSONファイルをcredentials.jsonという名前に変更してカレントフォルダに保存
  • nohup go run github.com/dtylman/gitmoo-goog@latest -folder-format "2016/01" -include-exif &でバックグラウンド起動
  • 動作状況確認のため、tail -f nohup.logしておきます
  • 表示されるURLを開き、Googleアカウントの選択またはログイン
  • 安全じゃない警告を詳細からバイパスし承認操作を行うと、http://127.0.0.1:8080/...にリダイレクトされます
  • もしgitmoo-googがSSH経由でリモート起動の場合、自動でトークン取得できないのでこの時のURLをコピーしておく
  • 別のSSHターミナルを開いて「curl -L "http://127.0.0.1:8080/..."」(ダブルクオートで囲む必要あり)とするとトークン取得できる
  • カレントフォルダにtoken.jsonが保存されるので、これで当分gitmoo-googが動作可能になる
  • (起動しないままある程度期間を置くと上記のトークン取得手順をもう一度やる必要がある)
  • Googleフォトにある写真と動画とメタデータをどんどんカレントフォルダにダウンロードしていく
  • APIのクォータを消費するので最後まで走らせるあたりを付けたら一気に実行したほうがよさそう
  • (何度も試し試しやっているとAPIのクォータが枯渇する)

引き込んだ結果

# du -hd 1
948.0K  ./1997 ↓ ガラケーメイン
5.0M    ./1998
35.0M   ./1999
3.1M    ./2000
43.6M   ./2001
187.8M  ./2002
61.0M   ./2003
18.5M   ./2004
280.9M  ./2005
235.2M  ./2006
505.6M  ./2007
1.3G    ./2008 ↓ iPod Touch
254.0M  ./2009
894.5M  ./2010
6.9G    ./2011 ↓ デジカメメイン(動画をほとんど撮らない)
8.3G    ./2012
5.8G    ./2013
9.0G    ./2014
186.6M  ./2015
268.2M  ./2016
267.9M  ./2017
236.0M  ./2018
20.9G   ./2019 ↓ iPhoneで撮るようになった(動画で撮ることが増えた)
48.2G   ./2020
40.2G   ./2021
90.3G   ./2022
93.4G   ./2023
25.7G   ./2024
353.4G  .

Googleフォト上のサイズ190GBのファイルのダウンロードで結果占有ディスク量は378GBなのでちょうど2倍のダウンロードサイズになった。アイテム数は35万を超えていましたがダウンロードと展開には28時間程度で済みました。

しかし、35万件のPhotoPrismのAIによるインデックス処理はめちゃくちゃ時間がかかりそうです(現在丸2日経過しましたが、まだ3万件のスキャンが終わった程度でこのままだと1か月くらいかかる?)。

gitmoo-googは新しいものからダウンロード、PhotoPrismのスキャンはアルファベット順に行うー>完了またはキャンセル後追加処理で人の顔認識などの追加処理が走るという感じでした。

cloudflared tunnnel の設定

CLIインストール:

curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/bin/cloudflared

ログインとホスト証明書の取得:

# cloudflared tunnel login
Please open the following URL and log in with your Cloudflare account:

https://dash.cloudflare.com/argotunnel?aud=&callback=https%3A%2F%2Flogin.cloudflareaccess.org%2F####%3D

Leave cloudflared running to download the cert automatically.
You have successfully logged in.
If you wish to copy your credentials to a server, they have been saved to:
/root/.cloudflared/cert.pem

トンネルの作成:

# cloudflared tunnel create photoprism
Tunnel credentials written to /root/.cloudflared/########-####-####-####-############.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.

Created tunnel photoprism with id ########-####-####-####-############
photostorage:~# cloudflared tunnel route dns ########-####-####-####-############ photoprism.your.domain

設定の保存「/root/photoprism.yml」:

photostorage.yml

tunnel: ########-####-####-####-############
credentials-file: /root/.cloudflared/########-####-####-####-############.json

ingress:
    - hostname: photoprism.your.domain
      service: http://127.0.0.1:2342
    - service: http_status:404

/etc/init.d/cloudflared.photos

#!/sbin/openrc-run

depend() {
        use logger dns net
        after firewall
        need net
        need photoprism
}

name=$RC_SVCNAME
command=/usr/bin/cloudflared
command_args="--pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s --config /root/photostorage.yml tunnel run"
command_user="root"
pidfile="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
command_background="yes"

起動と自動起動設定

> rc-service cloudflared.photos start
> rc-update add cloudflared.photos

https://photoprism.your.domainを開き、「ID: admin、Password: insecure」でログインすることで以下のような表示が出るのを確認(初期では写真なし)しましょう。


PCの場合


スマホの場合

すぐに、パスワードの変更はやっておきましょう。

インデックス処理

  • 左メニュー欄の「ライブラリ(Library)」を選択
  • 真ん中の「インポート(Import)」を選択
  • 「ファイルを移動」にチェックして「インポート(Import)」を押す
  • この処理が重い・・・

バックアップの仕掛け

以下のファイルを作成しておきます。

/etc/conf.d/syncthing

SYNCTHING_ARGS="serve --no-browser --home=/mnt/storage"
SYNCTHING_USER=root
> rc-service syncthing start

syncthingの初期設定

/mnt/storage/config.xmlを編集

  • 127.0.0.10.0.0.0に置換
  • ~/mnt/storageに置換
  • 保存
> rc-service syncthing restart
> rc-update add syncthing

http://[HOSTNAME].local:8384をブラウザで開きます。

Picturesを追加すると以下のような表示:

Picturesをひらいて編集をクリックし、共有タブを開くと:

ここにはLAN内のSyncthingデバイスが表示されますのでチェックを入れて共有を開始します。
すると、要求されてデバイスでは要求の通知が表示され受け入れると同期を開始します。

もし、複数の同期デバイスとでファイルフォーマットが異なるという場合、パーミッションの同期をオフにした方が良いかもしれません。また、OSが作成するようなキャッシュフォルダなども除外する設定を入れておく方が同期トラブルに遭いにくいです。

実際の運用

  • スマホで写真や動画を撮影
  • 家に帰ったら同期&削除アプリを起動して2タップして充電器にセットして放置
  • 閲覧はPWAアプリを起動して観る
  • バックアップの動作状況が良好であることは時々確認
  • 5年後あたりに新規のSSDの差し替え版を作成してバックアップで復旧する
  • gitmoo-googの変換方針とPhotoPrismの変換方針がミスマッチ
    • gitmoo-googは「YYYY/Month」形式のフォルダ配下にいれる
    • PhotoPrismは外部から取り込むとき「YYYY/MM」形式のフォルダ配下にいれる
    • gitmoo-googはHEICやJpegをJFIF形式とJSONに変換、MOVをF4Vに変換する
    • PhotoPrismはJFIFやF4VからサムネをJpegにして保存する
    • gitmoo-googは新しいものから古いものの順番にダウンロード
    • PhotoPrismのインデックス処理は存在するなかで古いものから順に行う

まとめ

  • どうしても自前のストレージに保存する以上、手放しでフォトストレージが維持できるわけじゃない
  • しかし、下手にRAIDで保全とかって実際の問題発生時に復旧がちゃんとできるかは不安がありすぎる
  • PhotoPrismの全スキャンは現状結構重い処理で30万アイテムのスキャンには数十日かかかりそう
  • AIが重いとしたらマシンパワーの良い環境なら改善されるのかも?
  • PWAアプリは思ったよりもサクサク動く検索も十分早い
  • 同期アプリは自動削除機能が本家よりも楽だった
  • Syncthingの同期はまぁまぁ堅牢、ファイルロックなどでトラブってもあとでリカバリが効く
  • 今回採用したツール群のほとんどがGo製だった
    • gitmoo-goog
    • cloudflaredコマンドラインツール
    • PhotoPrism
    • Syncthing
    • もちろんDockerも
  • まだ完全に取り込みが完了していないので、うまく運用開始できるかどうかは確認中です

Discussion

NoboNoboNoboNobo

まだ、安定動作の確認に至ってないんだけど、SSDが切断される問題に悩まされていた。
RaspberryPiのLinuxカーネルには「USB Attached SCSI Protocol (UASP)」に問題があるみたいで、
RaspberryPiとUSB3接続SSDなどの組み合わせでUASPモードで動こうとしてしまう。
ここをBulk Only Transport (BOT)に切り替える必要がありそう。
切り替えてみて数日、ようやくSSDが安定動作している。

NoboNoboNoboNobo

大量のメディアファイルを取り込んでいる途中でOOMキラーによって殺されている。
スワップの設定を試みる。

NoboNoboNoboNobo

GoogleフォトはAPI経由の場合、一日につき75000件のダウンロードしかできない。
75000件づつ数日かけてダウンロードする必要あり。
日本時間の16時にこのクオータはリセットされる。

NoboNoboNoboNobo

「Photo Uploader for PhotoPrism」が認証通らなくなってるなぁ。どうしてだろう?

しかし、「Möbius Sync」というアプリ

https://mobiussync.com/

これを使うと、Syncthing経由で自動的にカメラロールの写真を同期してくれる。
これはこれで良いかも。

NoboNoboNoboNobo

いろいろわかったことがある。

  • G-PhotosのAPI経由でのダウンロードはロケーション情報が欠落する
  • 2021/5末までは無料ストレージ期間だったのでこれ以前の写真を削除しても容量の空きは増えない
  • スター付与やアルバムに入れていても削除するとそれぞれからも見れなくなる
  • なので、アルバムなどの管理は別途する必要がある?