migrate-to-photovault
A one-shot CLI written in Go that migrates photos and videos from any S3 bucket into the PhotoVault storage layout, generating 150x150 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 S3 LastModified)
-
Allocate destination key
{prefix}media/YYYY/MM/DD/HH-mm-ss-mmm[_N].extusing ETag comparison:
_2, _3ā¦
- Generate 150x150 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 (150x150 JPEG)
āāā 14-30-45-123_2.jpg ā photo thumbnail same-millisecond collision (150x150 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 150x150 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.