summaryrefslogtreecommitdiff
path: root/scripts/immich-export
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-31 23:03:17 +0300
committerPaul Buetow <paul@buetow.org>2026-03-31 23:03:17 +0300
commitf3c1292c879fdb2b9544dec11847882217d96e1d (patch)
tree31783e70f21686e3bd56e6520d590a4592263515 /scripts/immich-export
parent4e6273ef3758fa0cfaea4d8e8f885eff1720710c (diff)
Add immich-export script to download images from Immich by date range
Exports images (no videos) from paul and albena accounts for a configurable date range via the Immich REST API, skipping already-downloaded files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'scripts/immich-export')
-rwxr-xr-xscripts/immich-export84
1 files changed, 84 insertions, 0 deletions
diff --git a/scripts/immich-export b/scripts/immich-export
new file mode 100755
index 0000000..38fe251
--- /dev/null
+++ b/scripts/immich-export
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+# Export all Immich assets for given accounts within a date range.
+# Usage: immich-export.sh
+# Accounts and API keys are hardcoded below; output goes to DEST_DIR.
+
+set -euo pipefail
+
+IMMICH_URL="http://immich.f3s.lan.buetow.org"
+DEST_DIR="/home/paul/Pictures/Pictures.Processing"
+DATE_AFTER="2026-01-01T00:00:00.000Z"
+DATE_BEFORE="2026-03-31T23:59:59.000Z"
+PAGE_SIZE=100
+
+declare -A ACCOUNTS=(
+ ["paul"]="$(cat ~/.immich_paul_key)"
+ ["albena"]="$(cat ~/.immich_albena_key)"
+)
+
+mkdir -p "$DEST_DIR"
+
+download_assets_for_account() {
+ local account="$1"
+ local api_key="$2"
+ local account_dir="$DEST_DIR/$account"
+ mkdir -p "$account_dir"
+
+ echo "==> Exporting account: $account"
+
+ local page=1
+ local downloaded=0
+ local skipped=0
+
+ while true; do
+ # Fetch one page of assets matching the date range
+ local response
+ response=$(curl -sf -X POST "$IMMICH_URL/api/search/metadata" \
+ -H "x-api-key: $api_key" \
+ -H "Content-Type: application/json" \
+ -d "{\"takenAfter\":\"$DATE_AFTER\",\"takenBefore\":\"$DATE_BEFORE\",\"type\":\"IMAGE\",\"size\":$PAGE_SIZE,\"page\":$page}")
+
+ # Note: the 'total' field in the response reflects only the current page size,
+ # not the grand total — so we track our own running count instead.
+ local next_page
+ next_page=$(echo "$response" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['assets'].get('nextPage',''))")
+
+ # Extract asset IDs and original filenames, then download each
+ while IFS=$'\t' read -r asset_id filename; do
+ local dest="$account_dir/$filename"
+ if [[ -f "$dest" ]]; then
+ ((skipped++)) || true
+ continue
+ fi
+
+ # Download the original file
+ if curl -sf -o "$dest" \
+ -H "x-api-key: $api_key" \
+ "$IMMICH_URL/api/assets/$asset_id/original"; then
+ ((downloaded++)) || true
+ echo " [page $page] downloaded #$downloaded: $filename"
+ else
+ echo " ERROR: failed to download $asset_id ($filename)" >&2
+ rm -f "$dest" # Remove partial file on failure
+ fi
+ done < <(echo "$response" | python3 -c "
+import json, sys
+d = json.load(sys.stdin)
+for item in d['assets']['items']:
+ print(item['id'] + '\t' + item['originalFileName'])
+")
+
+ # Stop when there are no more pages
+ [[ -z "$next_page" ]] && break
+ page="$next_page"
+ done
+
+ echo " Done: $downloaded downloaded, $skipped skipped (already exist)"
+}
+
+for account in "${!ACCOUNTS[@]}"; do
+ download_assets_for_account "$account" "${ACCOUNTS[$account]}"
+done
+
+echo ""
+echo "Export complete -> $DEST_DIR"