Add automated import-photos.sh helper script for existing photos

When Strategy 1 (unified library) is chosen with existing photos, the
setup now generates an import-photos.sh script that automates the entire
import process:

- Verifies Immich is running (API health check)
- Prompts for API key with validation
- Configures the storage template via API automatically (uses python3
  for JSON manipulation, falls back to manual instructions)
- Installs immich-cli via npx if Node.js available, offers to install
  Node.js if not
- Runs the upload with --recursive from the baked-in source path
- Shows photo count and progress

Also:
- Ask where existing photos currently live (separate from library path)
- Use that path for both the external library mount (Strategy 2) and
  the import script source (Strategy 1)
- Post-setup instructions reduced to 3 steps: create account, get API
  key, run import script
- .env comments reference the import script instead of raw CLI commands

https://claude.ai/code/session_01NAtxAkC5t6YVb3gcP1VcX8
This commit is contained in:
Claude
2026-02-19 03:23:15 +00:00
parent 430a30ca18
commit ee145dc736
+226 -30
View File
@@ -2830,8 +2830,16 @@ else
IMMICH_STRATEGY=""
EXTERNAL_LIBRARY=""
EXISTING_PHOTOS_SOURCE=""
if [ "$HAS_EXISTING_PHOTOS" = "y" ] || [ "$HAS_EXISTING_PHOTOS" = "Y" ]; then
echo ""
echo " Where are your existing photos now?"
echo " Default: $PHOTOS_DIR"
prompt_text " Existing photos path:" "$PHOTOS_DIR" EXISTING_PHOTOS_SOURCE 2>/dev/null || EXISTING_PHOTOS_SOURCE="$PHOTOS_DIR"
EXISTING_PHOTOS_SOURCE="${EXISTING_PHOTOS_SOURCE/#\~/$ACTUAL_HOME}"
EXISTING_PHOTOS_SOURCE="${EXISTING_PHOTOS_SOURCE%/}"
echo ""
echo " How should Immich handle your existing photos?"
echo ""
@@ -2862,11 +2870,11 @@ else
if [ "$IMMICH_STRATEGY" = "2" ]; then
# External library: existing photos stay in place, uploads go alongside
UPLOAD_LOCATION="$PRIMARY_DRIVE_PATH/immich-uploads"
EXTERNAL_LIBRARY="$PHOTOS_DIR"
EXTERNAL_LIBRARY="$EXISTING_PHOTOS_SOURCE"
echo ""
echo " Your photo library:"
echo " $PHOTOS_DIR"
echo " $EXISTING_PHOTOS_SOURCE"
echo " ├── [your existing folders] ← indexed by Immich (read-only)"
echo " $PRIMARY_DRIVE_PATH"
echo " └── immich-uploads/ ← new phone/web uploads"
@@ -2877,7 +2885,7 @@ else
EXTERNAL_LIBRARY=""
echo ""
if [ "$HAS_EXISTING_PHOTOS" = "y" ] || [ "$HAS_EXISTING_PHOTOS" = "Y" ]; then
if [ -n "$EXISTING_PHOTOS_SOURCE" ]; then
echo " All photos (existing + new) will live in:"
else
echo " All photos will be stored in:"
@@ -3072,18 +3080,14 @@ IMMICH_ENV
# IMMICH CONFIGURATION — Unified Library
# ============================================================
#
# STORAGE TEMPLATE (configure in Immich web UI):
# Admin → Settings → Storage Template → Enable
# Template: {{y}}/{{MM}}/{{filename}}
# This organizes photos into: photos/2026/01/filename.jpg
# All photos (imported + new uploads) are stored in one location.
# Storage template organizes files by date automatically.
#
# IMPORTING EXISTING PHOTOS:
# npm install -g @immich/cli
# immich login http://localhost:2283/api <your-api-key>
# immich upload --recursive /path/to/existing/photos
# To import existing photos, run:
# $IMMICH_DIR/import-photos.sh
#
# The CLI reads EXIF data and sets correct dates automatically.
# Get your API key: User Settings → API Keys
# The import script handles storage template configuration,
# CLI installation, and uploads with EXIF date preservation.
#
# ============================================================
@@ -3107,6 +3111,195 @@ IMMICH_ENV
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$UPLOAD_LOCATION" 2>/dev/null || true
[ -n "$EXTERNAL_LIBRARY" ] && chown -R "$ACTUAL_USER:$ACTUAL_USER" "$EXTERNAL_LIBRARY" 2>/dev/null || true
# Create import helper script for Strategy 1 with existing photos
if [ "$IMMICH_STRATEGY" != "2" ] && [ -n "$EXISTING_PHOTOS_SOURCE" ]; then
cat > "$IMMICH_DIR/import-photos.sh" << 'IMPORT_SCRIPT_HEAD'
#!/bin/bash
################################################################################
# Immich Photo Import Script
# Generated by ubuntu-post-install
#
# This script imports your existing photo collection into Immich with proper
# EXIF date preservation. Photos are uploaded through the API, which triggers
# Immich's metadata extraction pipeline — dates, GPS, camera info are all
# read from the original files.
################################################################################
IMPORT_SCRIPT_HEAD
cat >> "$IMMICH_DIR/import-photos.sh" << IMPORT_SCRIPT_VARS
IMMICH_URL="http://localhost:2283"
SOURCE_DIR="$EXISTING_PHOTOS_SOURCE"
IMPORT_SCRIPT_VARS
cat >> "$IMMICH_DIR/import-photos.sh" << 'IMPORT_SCRIPT_BODY'
echo ""
echo "┌─────────────────────────────────────────────────────────────────┐"
echo "│ IMMICH PHOTO IMPORT │"
echo "└─────────────────────────────────────────────────────────────────┘"
echo ""
# ── Preflight checks ────────────────────────────────────────────
# Check if Immich is running
echo "Checking Immich server..."
if ! curl -sf "$IMMICH_URL/api/server/ping" > /dev/null 2>&1; then
echo ""
echo " ✗ Immich is not running at $IMMICH_URL"
echo " Start it with: cd $(dirname "$0") && docker compose up -d"
echo ""
exit 1
fi
echo " ✓ Immich is running"
# Check source directory
if [ ! -d "$SOURCE_DIR" ]; then
echo ""
echo " ✗ Source directory not found: $SOURCE_DIR"
echo " Update SOURCE_DIR in this script if your photos moved."
echo ""
exit 1
fi
PHOTO_COUNT=$(find "$SOURCE_DIR" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.heic" -o -iname "*.heif" -o -iname "*.webp" -o -iname "*.gif" -o -iname "*.tiff" -o -iname "*.bmp" -o -iname "*.mp4" -o -iname "*.mov" -o -iname "*.avi" -o -iname "*.mkv" -o -iname "*.webm" \) 2>/dev/null | wc -l)
echo " ✓ Found ~$PHOTO_COUNT photos/videos in $SOURCE_DIR"
# ── Get API key ─────────────────────────────────────────────────
echo ""
echo " Before continuing, you need an API key from Immich:"
echo " 1. Open $IMMICH_URL in your browser"
echo " 2. Log in (create admin account if first visit)"
echo " 3. Click your avatar → Account Settings → API Keys"
echo " 4. Create a new key and copy it"
echo ""
API_KEY="${1:-}"
if [ -z "$API_KEY" ]; then
read -p " Enter your Immich API key: " API_KEY
fi
if [ -z "$API_KEY" ]; then
echo " ✗ API key is required"
exit 1
fi
# Verify API key works
echo ""
echo " Verifying API key..."
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" -H "x-api-key: $API_KEY" "$IMMICH_URL/api/users/me" 2>/dev/null)
if [ "$HTTP_CODE" != "200" ]; then
echo " ✗ Invalid API key (server returned $HTTP_CODE)"
echo " Double-check the key and try again."
exit 1
fi
echo " ✓ API key valid"
# ── Configure storage template via API ──────────────────────────
echo ""
echo " Configuring storage template ({{y}}/{{MM}}/{{filename}})..."
# Get current config, enable storage template
CURRENT_CONFIG=$(curl -sf -H "x-api-key: $API_KEY" "$IMMICH_URL/api/system-config" 2>/dev/null)
if [ -n "$CURRENT_CONFIG" ] && command -v python3 &> /dev/null; then
UPDATED_CONFIG=$(echo "$CURRENT_CONFIG" | python3 -c "
import sys, json
config = json.load(sys.stdin)
config['storageTemplate']['enabled'] = True
config['storageTemplate']['template'] = '{{y}}/{{MM}}/{{filename}}'
json.dump(config, sys.stdout)
" 2>/dev/null)
if [ -n "$UPDATED_CONFIG" ]; then
RESULT=$(curl -sf -o /dev/null -w "%{http_code}" -X PUT \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
"$IMMICH_URL/api/system-config" \
-d "$UPDATED_CONFIG" 2>/dev/null)
if [ "$RESULT" = "200" ]; then
echo " ✓ Storage template configured automatically"
else
echo " ⚠ Could not set storage template via API"
echo " Set it manually: Admin → Settings → Storage Template → Enable"
echo " Template: {{y}}/{{MM}}/{{filename}}"
fi
else
echo " ⚠ Could not parse config. Set storage template manually:"
echo " Admin → Settings → Storage Template → Enable"
echo " Template: {{y}}/{{MM}}/{{filename}}"
fi
else
echo " ⚠ Set storage template manually before importing:"
echo " Admin → Settings → Storage Template → Enable"
echo " Template: {{y}}/{{MM}}/{{filename}}"
fi
# ── Install immich-cli if needed ────────────────────────────────
echo ""
IMMICH_CMD=""
if command -v immich &> /dev/null; then
IMMICH_CMD="immich"
echo " ✓ Immich CLI already installed"
elif command -v npx &> /dev/null; then
IMMICH_CMD="npx --yes @immich/cli"
echo " ✓ Using npx to run Immich CLI (no global install needed)"
elif command -v npm &> /dev/null; then
echo " Installing Immich CLI..."
npm install -g @immich/cli 2>/dev/null && IMMICH_CMD="immich"
fi
if [ -z "$IMMICH_CMD" ]; then
echo " Node.js is required for the Immich CLI."
read -p " Install Node.js now? (y/n): " INSTALL_NODE
if [ "$INSTALL_NODE" = "y" ] || [ "$INSTALL_NODE" = "Y" ]; then
sudo apt-get update -qq && sudo apt-get install -y -qq nodejs npm 2>/dev/null
if command -v npx &> /dev/null; then
IMMICH_CMD="npx --yes @immich/cli"
echo " ✓ Node.js installed"
else
echo " ✗ Node.js installation failed"
exit 1
fi
else
echo ""
echo " You can install Node.js later and run this script again,"
echo " or install manually:"
echo " sudo apt install nodejs npm"
echo " npx @immich/cli login $IMMICH_URL/api <api-key>"
echo " npx @immich/cli upload --recursive \"$SOURCE_DIR\""
exit 0
fi
fi
# ── Run the import ──────────────────────────────────────────────
echo ""
echo " Logging into Immich..."
$IMMICH_CMD login "$IMMICH_URL/api" "$API_KEY"
if [ $? -ne 0 ]; then
echo " ✗ Login failed"
exit 1
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Starting import from: $SOURCE_DIR"
echo " Importing ~$PHOTO_COUNT files. This may take a while."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
$IMMICH_CMD upload --recursive "$SOURCE_DIR"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Import complete!"
echo " View your photos at: $IMMICH_URL"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
IMPORT_SCRIPT_BODY
chmod +x "$IMMICH_DIR/import-photos.sh"
chown "$ACTUAL_USER:$ACTUAL_USER" "$IMMICH_DIR/import-photos.sh"
fi
echo ""
echo "✓ Immich configured at $IMMICH_DIR"
echo " Photo library: $PHOTOS_DIR"
@@ -3131,12 +3324,12 @@ IMMICH_ENV
echo ""
echo " 1. Open http://localhost:2283 and create your admin account"
echo ""
echo " 2. Configure storage template (keeps files organized by date):"
echo " Admin → Settings → Storage Template → Enable"
echo " Template: {{y}}/{{MM}}/{{filename}}"
echo ""
if [ "$IMMICH_STRATEGY" = "2" ]; then
echo " 2. Enable storage template (keeps new uploads organized):"
echo " Admin → Settings → Storage Template → Enable"
echo " Template: {{y}}/{{MM}}/{{filename}}"
echo ""
echo " 3. Set up your existing photo library:"
echo " Admin → External Libraries → Create Library"
echo " Import path: /usr/src/app/external"
@@ -3144,20 +3337,23 @@ IMMICH_ENV
echo ""
echo " New uploads from the mobile app or web UI are stored"
echo " separately in: $UPLOAD_LOCATION"
elif [ -n "$EXISTING_PHOTOS_SOURCE" ]; then
echo " 2. Get your API key:"
echo " Click avatar → Account Settings → API Keys → New API Key"
echo ""
echo " 3. Run the import script:"
echo " $IMMICH_DIR/import-photos.sh"
echo ""
echo " The script handles everything: configures storage template,"
echo " installs the CLI tool, and uploads all photos from"
echo " $EXISTING_PHOTOS_SOURCE with EXIF dates preserved."
else
if [ "$HAS_EXISTING_PHOTOS" = "y" ] || [ "$HAS_EXISTING_PHOTOS" = "Y" ]; then
echo " 3. Import your existing photos (preserves EXIF dates):"
echo " npm install -g @immich/cli"
echo " immich login http://localhost:2283/api <your-api-key>"
echo " immich upload --recursive /path/to/existing/photos"
echo ""
echo " Get your API key: User Settings → API Keys"
echo " After import, all photos live in one library at:"
echo " $PHOTOS_DIR"
else
echo " That's it. Upload photos from the mobile app or web UI."
echo " All photos are stored in: $PHOTOS_DIR"
fi
echo " 2. Enable storage template (keeps files organized by date):"
echo " Admin → Settings → Storage Template → Enable"
echo " Template: {{y}}/{{MM}}/{{filename}}"
echo ""
echo " That's it. Upload photos from the mobile app or web UI."
echo " All photos are stored in: $PHOTOS_DIR"
fi
echo ""
fi