-
-
Save Thoroslives/ca5d8e1efd15111febc1e7b34ac72668 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3 | |
# Note: you might need to run "pip install requests halo tabulate tqdm" if these dependencies are missing on your machine | |
import argparse | |
import json | |
import requests | |
from urllib.parse import urlparse | |
from halo import Halo | |
from tabulate import tabulate | |
from tqdm import tqdm | |
def parse_arguments(): | |
parser = argparse.ArgumentParser(description='Fetch file report and delete orphaned media assets from Immich.') | |
# Make API key and Immich address arguments optional | |
parser.add_argument('--admin_apikey', help='Immich admin API key for fetching reports', nargs='?', default=None) | |
parser.add_argument('--user_apikey', help='User-specific Immich API key for deletion', nargs='?', default=None) | |
parser.add_argument('--immichaddress', help='Full address for Immich, including protocol and port', nargs='?', default=None) | |
parser.add_argument('--no_prompt', action='store_true', help='Delete orphaned media assets without confirmation') | |
return parser.parse_args() | |
def filter_entities(response_json, entity_type): | |
return [ | |
{'pathValue': entity['pathValue'], 'entityId': entity['entityId'], 'entityType': entity['entityType']} | |
for entity in response_json.get('orphans', []) if entity.get('entityType') == entity_type | |
] | |
def main(): | |
args = parse_arguments() | |
# Prompt for admin API key if not provided | |
admin_api_key = args.admin_apikey if args.admin_apikey else input('Enter the Immich admin API key: ') | |
# Prompt for user API key if not provided | |
user_api_key = args.user_apikey if args.user_apikey else input('Enter the Immich user API key for deletion: ') | |
# Prompt for Immich address if not provided | |
immich_server = args.immichaddress if args.immichaddress else input('Enter the full web address for Immich, including protocol and port: ') | |
if not admin_api_key or not user_api_key: | |
print("Both admin and user API keys are required.") | |
return | |
immich_parsed_url = urlparse(immich_server) | |
base_url = f'{immich_parsed_url.scheme}://{immich_parsed_url.netloc}' | |
api_url = f'{base_url}/api' | |
file_report_url = api_url + '/report' | |
headers = {'x-api-key': admin_api_key} | |
print() | |
spinner = Halo(text='Retrieving list of orphaned media assets...', spinner='dots') | |
spinner.start() | |
try: | |
response = requests.get(file_report_url, headers=headers) | |
response.raise_for_status() | |
spinner.succeed('Success!') | |
except requests.exceptions.RequestException as e: | |
spinner.fail(f'Failed to fetch assets: {str(e)}') | |
return | |
orphan_media_assets = filter_entities(response.json(), 'asset') | |
num_entries = len(orphan_media_assets) | |
if num_entries == 0: | |
print('No orphaned media assets found; exiting.') | |
return | |
if not args.no_prompt: | |
table_data = [[asset['pathValue'], asset['entityId']] for asset in orphan_media_assets] | |
print(tabulate(table_data, headers=['Path Value', 'Entity ID'], tablefmt='pretty')) | |
print() | |
summary = f'There {"is" if num_entries == 1 else "are"} {num_entries} orphaned media asset{"s" if num_entries != 1 else ""}. Would you like to delete {"them" if num_entries != 1 else "it"} from Immich? (yes/no): ' | |
user_input = input(summary).lower() | |
print() | |
if user_input not in ('y', 'yes'): | |
print('Exiting without making any changes.') | |
return | |
headers['x-api-key'] = user_api_key # Use user API key for deletion | |
with tqdm(total=num_entries, desc="Deleting orphaned media assets", unit="asset") as progress_bar: | |
for asset in orphan_media_assets: | |
entity_id = asset['entityId'] | |
asset_url = f'{api_url}/asset' | |
delete_payload = json.dumps({'force': True, 'ids': [entity_id]}) | |
headers = {'Content-Type': 'application/json', 'x-api-key': user_api_key} | |
try: | |
response = requests.delete(asset_url, headers=headers, data=delete_payload) | |
response.raise_for_status() | |
except requests.exceptions.HTTPError as e: | |
if response.status_code == 400: | |
print(f"Failed to delete asset {entity_id} due to potential API key mismatch. Ensure you're using the asset owners API key as the User API key.") | |
else: | |
print(f"Failed to delete asset {entity_id}: {str(e)}") | |
continue | |
progress_bar.update(1) | |
print('Orphaned media assets deleted successfully!') | |
if __name__ == '__main__': | |
main() |
Dear @Thoroslives, Can you help me, please?
I've only 2 users: Admin & Normal User
I have 12 Offline Paths:
and 18874 Untracked files:
I've used your script to remove the Offline Paths, but seems it doesn't work. :(
Do you have any suggestion?
I don't know if it is useful to know that before using the "default upload library" I've also tried the external libraries.
Thank you so much for all help you can give me! :)
(.venv) root@miniserver:~/immich-app# python clean-offline-paths.py Enter the Immich admin API key: <ADMIN-API-KEY> Enter the Immich user API key for deletion: <ADMIN-API-KEY> Enter the full web address for Immich, including protocol and port: http://192.168.1.86:2283 ✔ Success! +-------------------------------------------------------------------------------------------------------------------------+--------------------------------------+ | Path Value | Entity ID | +-------------------------------------------------------------------------------------------------------------------------+--------------------------------------+ | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/f9/6c/f96ca4df-18b5-4994-a797-3e66fdcafb26-preview.jpeg | f96ca4df-18b5-4994-a797-3e66fdcafb26 | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/71/04/7104908c-2c06-4b5d-be35-f7c7f9db347e-preview.jpeg | 7104908c-2c06-4b5d-be35-f7c7f9db347e | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/b3/e9/b3e9c3dc-1222-458a-bb89-c51a5639ce08-preview.jpeg | b3e9c3dc-1222-458a-bb89-c51a5639ce08 | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/93/b5/93b57fed-292c-47bb-b476-3f0493bf7022-preview.jpeg | 93b57fed-292c-47bb-b476-3f0493bf7022 | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/cc/30/cc30449a-cf44-410a-ae96-44ac6a3b1c46-preview.jpeg | cc30449a-cf44-410a-ae96-44ac6a3b1c46 | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/55/79/5579e19c-67b6-40e3-8aeb-5bd1f037ac79-preview.jpeg | 5579e19c-67b6-40e3-8aeb-5bd1f037ac79 | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/d7/5b/d75bd5b4-baff-4dd7-a941-ef6e8724f8df-preview.jpeg | d75bd5b4-baff-4dd7-a941-ef6e8724f8df | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/80/4d/804d45c8-b633-4108-abe5-b67c696d2398-preview.jpeg | 804d45c8-b633-4108-abe5-b67c696d2398 | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/89/41/89417320-c34c-4d39-be7b-5dabd8f2d393-preview.jpeg | 89417320-c34c-4d39-be7b-5dabd8f2d393 | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/29/b8/29b88ec7-7aa1-46bb-9f20-9a938426144b-preview.jpeg | 29b88ec7-7aa1-46bb-9f20-9a938426144b | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/e7/1c/e71ca9a9-5690-4447-92c6-2ed1c9e24329-preview.jpeg | e71ca9a9-5690-4447-92c6-2ed1c9e24329 | | /usr/src/app/upload/thumbs/f4dad349-3b95-4cf5-86ca-143ea74acb18/3f/81/3f819148-8ec5-4feb-960c-36cbec31a1b6-preview.jpeg | 3f819148-8ec5-4feb-960c-36cbec31a1b6 | +-------------------------------------------------------------------------------------------------------------------------+--------------------------------------+ There are 12 orphaned media assets. Would you like to delete them from Immich? (yes/no): yes Deleting orphaned media assets: 0%| | 0/12 [00:00<?, ?asset/s]Failed to delete asset f96ca4df-18b5-4994-a797-3e66fdcafb26: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset 7104908c-2c06-4b5d-be35-f7c7f9db347e: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset b3e9c3dc-1222-458a-bb89-c51a5639ce08: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset 93b57fed-292c-47bb-b476-3f0493bf7022: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset cc30449a-cf44-410a-ae96-44ac6a3b1c46: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset 5579e19c-67b6-40e3-8aeb-5bd1f037ac79: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset d75bd5b4-baff-4dd7-a941-ef6e8724f8df: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset 804d45c8-b633-4108-abe5-b67c696d2398: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset 89417320-c34c-4d39-be7b-5dabd8f2d393: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset 29b88ec7-7aa1-46bb-9f20-9a938426144b: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset e71ca9a9-5690-4447-92c6-2ed1c9e24329: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Failed to delete asset 3f819148-8ec5-4feb-960c-36cbec31a1b6: 400 Client Error: Bad Request for url: http://192.168.1.86:2283/api/asset Deleting orphaned media assets: 0%| | 0/12 [00:00<?, ?asset/s] Orphaned media assets deleted successfully!
Are the offline assets for the admin or the other user. As i can see you used the admin API for both scanning and deletion. Try running it again with admin API first the use the other users API as the second prompted one. Let me know how you go
In terms of untracked files have a look at this script which moves them out to a seperate folder where you can attempt to re upload or delete them https://github.com/clumsyCoder00/Immich-Tools/tree/main
Thank you @Thoroslives, I see that all untracked files are from the following paths:
/usr/src/app/upload/upload/
/usr/src/app/upload/thumbs/
/usr/src/app/upload/library/thumbs/
/usr/src/app/upload/library/encoded-video/
But, after to have checked at least 5 paths for the "encoded-video", I didn't found any files.
I think I can't resolve with the script you suggested me. What do you think about?
Thank you so much!
Thank you @Thoroslives, I see that all untracked files are from the following paths:
/usr/src/app/upload/upload/
/usr/src/app/upload/thumbs/
/usr/src/app/upload/library/thumbs/
/usr/src/app/upload/library/encoded-video/
But, after to have checked at least 5 paths for the "encoded-video", I didn't found any files.
I think I can't resolve with the script you suggested me. What do you think about?
Thank you so much!
Are those paths all showing as untracked in the repair page?
If so thay script i linked will move them all to a folder for you, just make sure your following the instructions properly
Are those paths all showing as untracked in the repair page?
Yes
If so that script I linked will move them all to a folder for you, just make sure your following the instructions properly.
But which files can the script move if the files don't exist?
Are those paths all showing as untracked in the repair page? Yes
If so that script I linked will move them all to a folder for you, just make sure your following the instructions properly. But which files can the script move if the files don't exist?
If they didnt exist i dont imagine they would show up as untracked files. Thats not my area of expertise unfortunately
Thank you so much @Thoroslives, I'll update you for the offline paths.
This Gist has been superseded by the repo I have created for it to explain in more detail usage and error handling of this script.
Immich Remove Offline Files
Dear @Thoroslives, Can you help me, please?
I've only 2 users: Admin & Normal User
I have 12 Offline Paths:
and 18874 Untracked files:
I've used your script to remove the Offline Paths, but seems it doesn't work. :(
Do you have any suggestion?
I don't know if it is useful to know that before using the "default upload library" I've also tried the external libraries.
Thank you so much for all help you can give me! :)