diff --git a/ubuntu-post-install.sh b/ubuntu-post-install.sh index ac3a08e..f1e0a03 100644 --- a/ubuntu-post-install.sh +++ b/ubuntu-post-install.sh @@ -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 -# 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 " + 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 " - 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