Go to file
MEGASOL\simon.adams 0d0d9fbb9f added quick start
2025-08-26 15:28:21 +02:00
.
2025-08-26 13:50:28 +02:00
.
2025-08-26 13:50:28 +02:00
2025-08-26 14:16:02 +02:00
2025-08-26 14:13:13 +02:00
2025-08-26 14:47:40 +02:00
.
2025-08-26 13:50:28 +02:00
2025-08-26 14:39:17 +02:00
.
2025-08-26 13:50:28 +02:00
2025-08-26 15:28:21 +02:00
.
2025-08-26 13:50:28 +02:00

Immich Drop Uploader

A tiny, zero-login web app for collecting photos/videos into your Immich server.

  • No accounts — open the page, drop files, done
  • Queue with progress via WebSocket (success / duplicate / error)
  • Duplicate prevention (local SHA1 cache + optional Immich bulkcheck)
  • Original dates preserved (EXIF → fileCreatedAt / fileModifiedAt)
  • Mobilefriendly
  • .envonly config (clean deploys) + Docker/Compose
  • Privacyfirst: never lists server media; UI only shows the current session

Table of contents


Quick start

Copy the docker-compose.yml and the .env file to a common folder, update the .env file before executing the CLI commands to quick start the container.

docker-compose.yml

version: "3.9"

services:
  immich-drop:
    image: ghcr.io/nasogaa/immich-drop:latest
    pull_policy: always
    container_name: immich-drop
    restart: unless-stopped

    # Load all variables from your repo's .env (PORT, IMMICH_BASE_URL, IMMICH_API_KEY, etc.)
    env_file:
      - ./.env

    # Expose the app on the same port as configured in .env (defaults to 8080)
    ports:
      - 8080:8080

    # Persist local dedupe cache (state.db) across restarts
    volumes:
      - immich_drop_data:/data

    # Simple healthcheck
    healthcheck:
      test: ["CMD-SHELL", "python - <<'PY'\nimport os,urllib.request,sys; url=f\"http://127.0.0.1:{os.getenv('PORT','8080')}/\";\ntry: urllib.request.urlopen(url, timeout=3); sys.exit(0)\nexcept Exception: sys.exit(1)\nPY"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

volumes:
  immich_drop_data:

.env

HOST=0.0.0.0
PORT=8080
IMMICH_BASE_URL=http://REPLACE_ME:2283/api
IMMICH_API_KEY=REPLACE_ME
MAX_CONCURRENT=3
STATE_DB=/data/state.db

CLI

docker compose pull
docker compose up -d

Architecture

  • Frontend: static HTML/JS (Tailwind). Drag & drop or “Choose files”, queue UI with progress and status chips.
  • Backend: FastAPI + Uvicorn.
    • Proxies uploads to Immich /assets
    • Computes SHA1 and checks a local SQLite cache (state.db)
    • Optional Immich dedupe via /assets/bulk-upload-check
    • WebSocket /ws pushes peritem progress to the current browser session only
  • Persistence: local SQLite (state.db) prevents reuploads across sessions/runs.

Folder structure

immich_drop/
├─ app/                    # FastAPI application (Python package)
│  ├─ __init__.py
│  ├─ app.py               # uvicorn app:app
│  └─ config.py            # loads .env from repo root
├─ frontend/               # static UI served at /static
│  ├─ index.html
│  └─ app.js
├─ main.py                 # thin entrypoint (python main.py)
├─ requirements.txt        # Python deps
├─ .env                    # single config file (see below)
├─ Dockerfile
├─ docker-compose.yml
└─ README.md

Requirements

  • Python 3.11
  • An Immich server + API key

Configuration (.env)

# Server
HOST=0.0.0.0 
PORT=8080

# Immich connection (include /api)
IMMICH_BASE_URL=http://REPLACE_ME:2283/api
IMMICH_API_KEY=REPLACE_ME
MAX_CONCURRENT=3

# Local dedupe cache
STATE_DB=./data/state.db         # local dev -> ./state.db (data folder is created in docker image)
# In Docker this is overridden to /data/state.db by docker-compose.yml

You can keep a checkedin /.env.example with the keys above for onboarding.


Quick start (Docker/Compose)

  1. Put your settings in .env at the repo root (see below).
  2. Build & run:
docker compose build
docker compose up -d
# open http://localhost:8080

A named volume stores /data/state.db so duplicates are remembered across container restarts.


How it works

  1. Queue Files selected in the browser are queued; each gets a clientside ID.
  2. Dedupe (local) Server computes SHA1 and checks state.db. If seen, marks as duplicate.
  3. Dedupe (server) Attempts Immich /assets/bulk-upload-check; if Immich reports duplicate, marks accordingly.
  4. Upload Multipart POST to ${IMMICH_BASE_URL}/assets with:
    • assetData, deviceAssetId, deviceId,
    • fileCreatedAt, fileModifiedAt (from EXIF when available; else lastModified),
    • isFavorite=false, filename, and header x-immich-checksum.
  5. Progress Backend streams progress via WebSocket to the same session.
  6. Privacy UI shows only the current sessions items. It never lists server media.

Mobile notes

  • Uses a labelwrapped input + short ghostclick suppression so the system picker does not reopen after tapping Done (fixes iOS/Android quirks).
  • Draganddrop is desktoporiented; on touch, use Choose files.

Troubleshooting

Uploads dont start on phones / picker reopens
Hardrefresh; current UI suppresses ghost clicks and resets the input.
If using a PWA/WebView, test in Safari/Chrome directly to rule out container quirks.

WebSocket connects/disconnects in a loop
Match schemes: ws:// for http://, wss:// for https://.
If behind a reverse proxy, ensure it forwards WebSockets.

413 Request Entity Too Large
If running behind nginx/Traefik/etc., bump body size limits (client_max_body_size for nginx).

/assets returns 401
Check IMMICH_API_KEY and ensure the base URL includes /api (e.g., http://<host>:2283/api).

Duplicate detected but you expect an upload
The proxy caches SHA1 in state.db. For a fresh run, delete that DB or point STATE_DB to a new file.


Security notes

  • The app is unauthenticated by design. Share the URL only with trusted people or keep it on a private network/VPN.
  • The Immich API key remains serverside; the browser never sees it.
  • No browsing of uploaded media; only ephemeral session state is shown.

Development

Run with live reload:

python main.py

The backend contains docstrings so you can generate docs later if desired.


License

MIT.

Description
No description provided
Readme MIT 1.7 MiB
Languages
Python 48%
HTML 36.7%
JavaScript 14.9%
Dockerfile 0.4%