Created
February 14, 2025 01:33
-
-
Save 2q2code/331265beed478d8d150781d8bc43536c to your computer and use it in GitHub Desktop.
Python script that removes non-HEIC duplicates of HEIC files from immich
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 | |
| import sys | |
| import json | |
| import argparse | |
| import requests | |
| # PREREQUISITES: | |
| # - python 3 of some variety (I used 3.12.7) | |
| # - python venv module (if using a virtual environment as described below) | |
| # - this script file | |
| # PREPARATION: | |
| # In your immich user profile, go to `Account Settings`, `API Keys`, and create a new API key for use | |
| # with this script (or use an existing one idc :)) - copy it down somewhere you can copy-paste from later. | |
| # Also note down the URL of your immich instance. You'll need that too. | |
| # INSTALLATION: | |
| # - create a virtual environment for this script (you can delete it later) on any computer that can access your immich instance | |
| # > python -m venv heic_dup_keep | |
| # - activate the virtual environment | |
| # > cd heic_dup_keep/bin; source ./activate | |
| # - copy this script into the directory (optional, but tidy) | |
| # > cp WHEREVER_THIS_IS/heic_dup_keep.py . | |
| # - make it executable | |
| # > chmod +x heic_dup_keep.py | |
| # - install the required library | |
| # > pip install requests | |
| # RUNNING: | |
| # - run the script with -h to see how to use it, but here is an example: | |
| # > ./heic_dup_keep.py https://immich.awesomeperson.net 12345NONSENSEAPIKEYHERE | |
| # CLEANUP: | |
| # - deactivate the virtual environment | |
| # > deactivate | |
| # - delete the virtual environment directory and all in it | |
| # > rm -r heic_dup_keep | |
| # - fin! | |
| def do_get(base_url, headers, url): | |
| try: | |
| response = requests.get(f'{base_url}/{url}', headers=headers) | |
| response.raise_for_status() | |
| return response.json() | |
| except requests.exceptions.RequestException as e: | |
| print(f"Error: {e}", file=sys.stderr) | |
| sys.exit(1) | |
| def delete_assets(base_url, headers, asset_list): | |
| ids = list(map(lambda item: item['id'], asset_list)) | |
| payload = json.dumps({ | |
| "force": False, | |
| "ids": ids | |
| }) | |
| requests.request("DELETE", f'{base_url}/assets', headers=headers, data=payload) | |
| print(f'Removed {len(ids)} assets') | |
| def list_dups(base_url, api_key): | |
| print('Retrieving duplicates list...') | |
| print(f"url: {base_url}, api_key: {api_key}\n") | |
| headers = { | |
| 'Content-Type': 'application/json', | |
| 'Accept': 'application/json', | |
| 'x-api-key': api_key | |
| } | |
| response = do_get(base_url, headers, 'duplicates') | |
| remove_assets_list = [] | |
| summary_list = [] | |
| for dupItem in response: | |
| # print(json.dumps(dupItem, indent=2)) | |
| heic_file = False | |
| temp_asset = [] | |
| duplicates_for_removal = [] | |
| heic_filename = '' | |
| for asset in dupItem['assets']: | |
| if 'image/heic' != asset['originalMimeType']: | |
| temp_asset.append(asset) | |
| duplicates_for_removal.append(asset['originalPath']) | |
| else: | |
| heic_file = True | |
| heic_filename = asset['originalPath'] | |
| if heic_file and temp_asset: | |
| remove_assets_list.extend(temp_asset) | |
| sum_obj = { | |
| "would_keep": heic_filename, | |
| "would_trash": duplicates_for_removal | |
| } | |
| summary_list.append(sum_obj) | |
| if remove_assets_list: | |
| print('Review the following list and confirm to remove:\n') | |
| print(json.dumps(summary_list, indent=2)) | |
| print(f"\nTotal number of unique images (duplicate sets): {len(summary_list)}") | |
| print(f"Total number of non-HEIC duplicate images to be removed: {len(remove_assets_list)}\n") | |
| user_input = input('\nProceed to trash the duplicates? (y/n)') | |
| if user_input.lower() in ('y','yes'): | |
| delete_assets(base_url, headers, remove_assets_list) | |
| else: | |
| print('Cancelled.') | |
| else: | |
| print('No duplicates found.') | |
| def main(): | |
| desc = "A slightly more user-friendly HEIC duplicate keeper,\n \ | |
| based on github user `dahool`'s original gist.\n \ | |
| This script will identify all existing duplicate images sets in\n \ | |
| your immich library that contain (at least) one HEIC file, print a summary of proposed action, then offer\n \ | |
| to move the non-HEIC duplicates to immich trash. All actions can be undone by restoring the items from trash in immich." | |
| epilog = "WARNING: Do NOT use this script if you use RAW files (and wish to keep them) like DNG etc - as it\n \ | |
| will remove them in favour of the HEIC files also. I will likely make a modified version of this for that case later.\n \ | |
| AUTHOR TAKES NO RESPONSIBILITY for effects of this script. Please use with caution and make sure you back up first." | |
| parser = argparse.ArgumentParser(description=desc, epilog=epilog) | |
| parser.add_argument("url", type=str, help="The URL of your immich instance e.g. https://immich.awesomeplace.com, or http://localhost:2283") | |
| parser.add_argument("apikey", type=str, help="An API key generated in your user profile, on your immich instance") | |
| args = parser.parse_args() | |
| list_dups(args.url + '/api', args.apikey) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment