migrate-to-photovault
A one-shot CLI written in Go that migrates photos and videos from your existing bucket into the PhotoVault layout, generating 512px JPEG thumbnails on the fly.
Run this one-liner in your terminal — it downloads the right binary for your OS and architecture and launches the setup wizard immediately:
curl -fsSL https://photo-vault-release.s3.cubbit.eu/migrator/install.sh | bash
No root required. Credentials are never written to disk.
▸ Prefer to inspect the script before running it?
Download, read, then execute manually:
curl -fsSL https://photo-vault-release.s3.cubbit.eu/migrator/install.sh -o install.sh
cat install.sh # inspect
bash install.sh # run when satisfied
install.sh content:
#!/bin/sh
set -e
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH=amd64 ;;
arm64|aarch64) ARCH=arm64 ;;
esac
URL="https://photo-vault-release.s3.cubbit.eu/migrator/migrate-to-photovault_${OS}_${ARCH}"
DEST="./migrate-to-photovault"
echo "Downloading migrate-to-photovault for ${OS}/${ARCH}…"
curl -fsSL "$URL" -o "$DEST"
chmod +x "$DEST"
echo "Done — run ./migrate-to-photovault --help to get started"
Scan destination
Before any upload starts, the tool lists all existing keys under
{dst-prefix}media/
and loads their ETags.
This lets it decide in a single pass whether a file is new, already uploaded, or a genuine collision.
List source objects
All objects in src-bucket under the optional
src-prefix are listed.
Keys already in the resume log are skipped instantly — no download.
Process each file (up to --concurrency in parallel)
- Download to a temporary file
- Extract DateTimeOriginal from EXIF (falls back to LastModified)
-
Allocate destination key
{prefix}media/YYYY/MM/DD/HH-mm-ss-mmm[_N].extusing ETag comparison:
_2, _3…
- Generate 512px JPEG thumbnail for all photo formats (including HEIC/RAW) (
imagingfor photos,ffmpegfor videos) - Upload thumbnail to
{prefix}thumbnails/… - Server-side
CopyObjectif same endpoint, otherwise re-upload - Optionally delete source with
--delete-source
Session & resume
The session config is saved at
~/.photovault-migrator/config.json
(no credentials — structural params only).
On the next run you are prompted:
Found a previous migration session:
├─ Source: my-bucket (prefix: "")
├─ Destination: photovault-prod (prefix: "photo-vault/")
├─ Started: 2024-03-15 14:30
├─ Last run: 2024-03-15 15:12
└─ Processed: 500 files
Resume? [Y/n]
Press Enter to resume.
Press n to start fresh (clears the resume log).
Pass --no-resume to skip the prompt entirely.
After migration your bucket will look like this (with the default photo-vault/ prefix):
photo-vault/
├── media/
│ └── 2024/
│ └── 03/
│ └── 15/
│ ├── 14-30-45-123.jpg ← original photo
│ ├── 14-30-45-123.mp4 ← original video
│ └── 14-30-45-123_2.jpg ← same-millisecond collision
└── thumbnails/
└── 2024/
└── 03/
└── 15/
├── 14-30-45-123.jpg ← photo thumbnail (512px JPEG)
├── 14-30-45-123_2.jpg ← photo thumbnail same-millisecond collision (512px JPEG)
└── 14-30-45-123_VIDEO.jpg ← video thumbnail
All photo formats supported
HEIC, RAW, and all other photo formats are fully supported. Thumbnails are always generated as 512px JPEG. The original file is moved as-is without any conversion.
Video thumbnails require ffmpeg
ffmpeg must be in your PATH. If it is not found, the video is still migrated but without a thumbnail.
Credentials are never persisted
The session config saved at ~/.photovault-migrator/ contains only structural parameters (buckets, prefix, region). Your access keys are never written to disk.
Safe interruption
Pressing Ctrl+C lets the current batch of in-flight files finish cleanly. The resume log is flushed before exit, so no work is lost.
Verbose logs
During the live UI, per-file details are written to migration.log in the current directory. Tail it in a second terminal for real-time output.
Ready to start?
Install the tool, run it once, and open the web gallery to browse your entire library.