Last active
January 13, 2026 21:31
-
-
Save sneumann/dbb1de943cd28fb52c8411f027e063b2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| immich_external_album.py | |
| Add all external library assets in Immich to a single album. | |
| License: CC0 | |
| Features: | |
| - Dry-run mode via --dry-run (no changes made) | |
| - Diagnostic output: counts, sample asset IDs | |
| - Pagination-safe for large libraries | |
| """ | |
| import requests | |
| import argparse | |
| import sys | |
| # ============================== | |
| # CONFIGURATION | |
| # ============================== | |
| IMMICH_URL = "http://immich.athome:2283" | |
| API_KEY = "T80.....................................OM" | |
| ALBUM_NAME = "External Library Name" | |
| PAGE_SIZE = 1000 # Number of assets per page | |
| # ============================== | |
| # ARGPARSE | |
| # ============================== | |
| parser = argparse.ArgumentParser(description="Add all external assets to an Immich album") | |
| parser.add_argument("--dry-run", action="store_true", help="Do not make changes, just show what would happen") | |
| args = parser.parse_args() | |
| DRY_RUN = args.dry_run | |
| HEADERS = { | |
| "x-api-key": API_KEY, | |
| "Content-Type": "application/json", | |
| } | |
| # ============================== | |
| # FUNCTIONS | |
| # ============================== | |
| def get_all_external_assets(): | |
| """Return list of all asset IDs from external library""" | |
| assets = [] | |
| page = 1 | |
| print("[INFO] Fetching external library assets...") | |
| while True: | |
| r = requests.post( | |
| f"{IMMICH_URL}/api/search/metadata", | |
| headers=HEADERS, | |
| json={ | |
| "page": page, | |
| "size": PAGE_SIZE, | |
| "isExternal": True | |
| }, | |
| ) | |
| if r.status_code != 200: | |
| print(f"[ERROR] Failed to fetch assets (status {r.status_code}): {r.text}") | |
| sys.exit(1) | |
| data = r.json() | |
| items = data.get("assets", {}).get("items", []) | |
| assets.extend(items) | |
| print(f"[DEBUG] Page {page}: {len(items)} assets fetched, total so far: {len(assets)}") | |
| if len(items) < PAGE_SIZE: | |
| break | |
| page += 1 | |
| asset_ids = [a["id"] for a in assets] | |
| print(f"[INFO] Total external assets found: {len(asset_ids)}") | |
| if asset_ids: | |
| print(f"[INFO] Sample asset IDs: {asset_ids[:10]}") | |
| return asset_ids | |
| def get_or_create_album(): | |
| """Return album ID; create album if missing (unless dry-run)""" | |
| r = requests.get(f"{IMMICH_URL}/api/albums", headers=HEADERS) | |
| if r.status_code != 200: | |
| print(f"[ERROR] Failed to fetch albums: {r.status_code} {r.text}") | |
| sys.exit(1) | |
| albums = r.json() | |
| for album in albums: | |
| if album["albumName"] == ALBUM_NAME: | |
| print(f"[INFO] Album already exists: {ALBUM_NAME} (ID {album['id']})") | |
| return album["id"] | |
| if DRY_RUN: | |
| print(f"[DRY-RUN] Would create album: {ALBUM_NAME}") | |
| return None | |
| r = requests.post( | |
| f"{IMMICH_URL}/api/albums", | |
| headers=HEADERS, | |
| json={"albumName": ALBUM_NAME}, | |
| ) | |
| if r.status_code != 200: | |
| print(f"[ERROR] Failed to create album: {r.status_code} {r.text}") | |
| sys.exit(1) | |
| album_id = r.json()["id"] | |
| print(f"[INFO] Created album: {ALBUM_NAME} (ID {album_id})") | |
| return album_id | |
| def add_assets_to_album(album_id, asset_ids): | |
| """Add asset IDs to album""" | |
| if DRY_RUN: | |
| print(f"[DRY-RUN] Would add {len(asset_ids)} assets to album") | |
| if asset_ids: | |
| print(f"[DRY-RUN] Sample asset IDs: {asset_ids[:10]}") | |
| return | |
| if not asset_ids: | |
| print("[INFO] No assets to add") | |
| return | |
| r = requests.put( | |
| f"{IMMICH_URL}/api/albums/{album_id}/assets", | |
| headers=HEADERS, | |
| json={"ids": asset_ids}, | |
| ) | |
| if r.status_code != 200: | |
| print(f"[ERROR] Failed to add assets to album: {r.status_code} {r.text}") | |
| sys.exit(1) | |
| print(f"[INFO] Added {len(asset_ids)} assets to album {ALBUM_NAME} (ID {album_id})") | |
| # ============================== | |
| # MAIN | |
| # ============================== | |
| if __name__ == "__main__": | |
| print(f"[INFO] Starting {'DRY-RUN' if DRY_RUN else 'LIVE'} mode...") | |
| asset_ids = get_all_external_assets() | |
| album_id = get_or_create_album() | |
| add_assets_to_album(album_id, asset_ids) | |
| print(f"[INFO] Script completed in {'DRY-RUN' if DRY_RUN else 'LIVE'} mode") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment