# Fuji Photo Processor Automatic photo processing pipeline for Fuji X-H2 photos: camera uploads via FTPS (FTP over TLS) to Synology NAS, files are automatically resized and organized, then picked up by Immich for library management. ## Architecture ``` ┌─────────────┐ FTPS ┌──────────────────┐ watchdog ┌─────────────┐ │ Fuji X-H2 │ ──────────→ │ Synology NAS │ ────────────→ │ Processor │ │ (camera) │ port 21 │ /volume1/data/photos/ │ polling │ container │ └─────────────┘ (TLS) │ incoming/ │ └──────┬──────┘ └──────────────────┘ │ │ resize + move ┌─────────────┴─────────────┐ │ │ ┌─────▼─────┐ ┌───────▼───────┐ │ /originals │ │ /processed │ │ YYYY/MM/ │ │ YYYY/MM/ │ │ (full-res) │ │ (1080px max) │ └────────────┘ └───────┬───────┘ │ ┌───────▼───────┐ │ Immich │ │ external lib │ └───────────────┘ ``` ## Prerequisites - Synology NAS with Docker (Container Manager) installed - FTPS (FTP over TLS) enabled on DSM - Immich running (for photo management) ## Setup ### 1. FTPS Server (DSM) 1. Open **Control Panel → File Services → FTP** 2. Enable FTP service on port **21** 3. Enable **FTP SSL/TLS encryption** (FTPS) 4. Set passive port range: **50000-50100** 5. Create a dedicated user `fujiftp` with write access to `/volume1/data/photos/incoming` ### 2. Camera Configuration (Fuji X-H2) Configure an FTP profile on the camera: | Setting | Value | |-----------------|--------------------------| | FTP Server Type | **FTPS** | | Server IP | `192.168.175.141` | | Port | `21` | | Passive Mode | **ON** | | Username | `fujiftp` | | Password | *(your password)* | | Upload Dir | `/data/photos/incoming` | | Auto Transfer | ON (or manual trigger) | > **Note**: Bij FTPS kun je via **FTP OPTIONAL SETTING → ROOT CERTIFICATE** een self-signed root certificaat laden als je NAS er een gebruikt. ### 3. Deploy ```bash ./deploy.sh ``` The deploy script will: - Create required directories on the NAS - Transfer and build the Docker image on the NAS - Start the container with appropriate volume mounts ### 4. Immich Integration 1. In Immich, go to **Administration → External Libraries** 2. Add a new library with import path: `/volume1/data/photos/processed` 3. Set scan interval (e.g., every 15 minutes) 4. Mount `/volume1/data/photos/processed` into the Immich container as a read-only volume ## Container Details ### Volumes | Container Path | Host Path | Purpose | |----------------|----------------------------------------|---------------------------| | `/incoming` | `/volume1/data/photos/incoming` | FTP upload landing zone | | `/originals` | `/volume1/data/photos/originals` | Full-resolution originals | | `/processed` | `/volume1/data/photos/processed` | Resized copies for Immich | | `/data` | `/volume2/docker/photo-processor/data` | SQLite tracking database | ### Environment Variables | Variable | Default | Description | |-----------------|---------|--------------------------------------| | `POLL_INTERVAL` | `30` | Filesystem poll interval in seconds | | `JPEG_QUALITY` | `85` | JPEG compression quality (1-100) | | `MAX_WIDTH` | `1080` | Maximum width for resized images | | `MAX_HEIGHT` | `1920` | Maximum height for resized images | | `TZ` | `Europe/Amsterdam` | Container timezone | ## Troubleshooting ### Check container logs ```bash ssh -i ../SynologyDocker/synology_ssh_key ssh@192.168.175.141 \ 'sudo /usr/local/bin/docker logs -f photo-processor' ``` ### Common Issues - **Files not detected**: Check that the FTP user has write permissions to `/volume1/data/photos/incoming`. The processor uses polling (not inotify) so there may be up to a 30-second delay. - **Permission denied on originals/processed**: Ensure the directories exist and are writable. The deploy script creates them automatically. - **Duplicate filenames**: The processor tracks files by filename in SQLite. If you re-upload a file with the same name, it will be skipped. Delete the entry from `/volume2/docker/photo-processor/data/processed.db` to reprocess. - **Container keeps restarting**: Check logs for Python errors. Common cause: missing directories or permission issues on volume mounts. - **EXIF data lost**: The processor preserves EXIF data from the original. If EXIF is missing, the original file may not have contained it (check camera settings).