commit cc8779be518983485ca848d325a845108de7a881 Author: Marco Realacci Date: Thu Apr 10 15:21:51 2025 +0200 initial commit diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..41c8359 --- /dev/null +++ b/.env.template @@ -0,0 +1,4 @@ +IMMICH_ALBUM_ID=6999805b-1c70-4881-ba65-785e4ae19654 +IMMICH_API_KEY=topsecretapykeypleasedontsteal +IMMICH_DOWNLOAD_PATH=/home/yourverycoolname/wallpapers +IMMICH_INSTANCE_URL=https://your-immich-server/api \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/immich-album-downloader.iml b/.idea/immich-album-downloader.iml new file mode 100644 index 0000000..8daaf6b --- /dev/null +++ b/.idea/immich-album-downloader.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..8c96dca --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..2c29d86 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..56b4cea --- /dev/null +++ b/main.py @@ -0,0 +1,89 @@ +import os +import requests +import logging +from pathlib import Path + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +# Environment variables +IMMICH_API_KEY = os.getenv('IMMICH_API_KEY') +IMMICH_INSTANCE_URL = os.getenv('IMMICH_INSTANCE_URL') +# ALBUM_NAME = os.getenv('IMMICH_ALBUM_NAME') +ALBUM_ID = os.getenv('IMMICH_ALBUM_ID') +DOWNLOAD_PATH = Path(os.getenv('IMMICH_DOWNLOAD_PATH', './downloads')) + + +# def get_album_id(): +# """Get album ID by name from Immich API""" +# headers = {'x-api-key': IMMICH_API_KEY} +# response = requests.get( +# f"{IMMICH_INSTANCE_URL}/albums", +# headers=headers, +# params={'albumName': ALBUM_NAME} +# ) +# response.raise_for_status() +# +# for album in response.json(): +# if album['albumName'] == ALBUM_NAME: +# return album['id'] +# raise ValueError(f"Album '{ALBUM_NAME}' not found") + + +def get_album_assets(album_id): + """Retrieve all assets in album with pagination""" + headers = {'x-api-key': IMMICH_API_KEY} + assets = [] + page = 1 + + while True: + response = requests.get( + f"{IMMICH_INSTANCE_URL}/albums/{album_id}", + headers=headers, + params={'page': page} + ) + response.raise_for_status() + + assets.extend(response.json()["assets"]) + total_pages = int(response.headers.get('X-Pagination-Count', 1)) + if page >= total_pages: + break + page += 1 + # break + + return assets + + +def download_assets(assets): + """Download missing assets to target directory""" + DOWNLOAD_PATH.mkdir(parents=True, exist_ok=True) + headers = {'x-api-key': IMMICH_API_KEY} + + for asset in assets: + filename = f"{asset['originalFileName']}" + filepath = DOWNLOAD_PATH / filename + + if not filepath.exists(): + logging.info(f"Downloading {filename}") + response = requests.get( + f"{IMMICH_INSTANCE_URL}/assets/{asset['id']}/original", + headers=headers, + stream=True + ) + response.raise_for_status() + + with open(filepath, 'wb') as f: + for chunk in response.iter_content(chunk_size=8192): + f.write(chunk) + else: + logging.debug(f"Skipping existing file {filename}") + + +if __name__ == "__main__": + try: + assets = get_album_assets(ALBUM_ID) + download_assets(assets) + logging.info(f"Sync complete. {len(assets)} assets processed") + except Exception as e: + logging.error(f"Sync failed: {str(e)}") + raise \ No newline at end of file