Add Immich import flow; ignore node_modules, secrets
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,3 +5,7 @@ photo_pipeline.db
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
test_flow.py
|
||||
node_modules/
|
||||
package.json
|
||||
package-lock.json
|
||||
.immich-key
|
||||
|
||||
83
immich_import.py
Normal file
83
immich_import.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""photo-pipeline: Immich import flow (v4).
|
||||
|
||||
Uploads sorted/staged photos to Immich (on .35) using the official @immich/cli.
|
||||
|
||||
Security: the API key comes from env IMMICH_API_KEY or ~/photo-pipeline/.immich-key
|
||||
(chmod 600). Never committed to git.
|
||||
|
||||
The CLI does checksum dedup natively (skips already-uploaded assets), so
|
||||
re-running is safe.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from prefect import flow, task
|
||||
|
||||
IMMICH_URL = os.environ.get("IMMICH_URL", "http://192.168.20.35:2283")
|
||||
KEY_FILE = Path(__file__).parent / ".immich-key"
|
||||
CLI = Path(__file__).parent / "node_modules/.bin/immich"
|
||||
|
||||
|
||||
def _api_key() -> str:
|
||||
key = os.environ.get("IMMICH_API_KEY")
|
||||
if key:
|
||||
return key
|
||||
if KEY_FILE.exists():
|
||||
return KEY_FILE.read_text().strip()
|
||||
raise RuntimeError("IMMICH_API_KEY env or ~/photo-pipeline/.immich-key required")
|
||||
|
||||
|
||||
@task
|
||||
def immich_login():
|
||||
"""Ensure CLI is authenticated (login-key is idempotent-ish; stores auth.yml)."""
|
||||
key = _api_key()
|
||||
r = subprocess.run(
|
||||
[str(CLI), "login-key", IMMICH_URL, key],
|
||||
capture_output=True, text=True,
|
||||
)
|
||||
print(f"immich login: rc={r.returncode} {r.stdout.strip()[:200]}")
|
||||
if r.returncode != 0:
|
||||
raise RuntimeError(f"immich login failed: {r.stderr[-500:]}")
|
||||
return True
|
||||
|
||||
|
||||
@task
|
||||
def immich_upload(src_dir: str, album: str | None = None, dry_run: bool = False) -> str:
|
||||
"""Upload assets from a directory to Immich.
|
||||
|
||||
Recursive, album-per-folder optional, dry-run supported. Immich dedups by checksum.
|
||||
"""
|
||||
cmd = [str(CLI), "upload", src_dir, "--recursive"]
|
||||
if album:
|
||||
cmd += ["--album-name", album]
|
||||
if dry_run:
|
||||
cmd += ["--dry-run"]
|
||||
r = subprocess.run(cmd, capture_output=True, text=True)
|
||||
out = r.stdout.strip()
|
||||
print(f"immich upload rc={r.returncode}")
|
||||
print(out[-2000:])
|
||||
if r.returncode != 0:
|
||||
raise RuntimeError(f"immich upload failed: {r.stderr[-500:]}")
|
||||
return out
|
||||
|
||||
|
||||
@flow(name="immich-import")
|
||||
def immich_import(
|
||||
src_dir: str,
|
||||
album: str | None = None,
|
||||
dry_run: bool = True,
|
||||
):
|
||||
"""Login to Immich and upload a staged folder. Dry-run by default — safe."""
|
||||
immich_login()
|
||||
result = immich_upload(src_dir, album=album, dry_run=dry_run)
|
||||
return {"uploaded": result}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
src = sys.argv[1] if len(sys.argv) > 1 else "/mnt/data/01_keep"
|
||||
dry = "--dry-run" in sys.argv or "--no-move" not in sys.argv
|
||||
immich_import(src, dry_run=dry)
|
||||
Reference in New Issue
Block a user