Skip to content

Instantly share code, notes, and snippets.

@Nomeyho
Created August 31, 2025 10:44
Show Gist options
  • Select an option

  • Save Nomeyho/fe5bb33238d10fe6dc3182c3b37f7c1a to your computer and use it in GitHub Desktop.

Select an option

Save Nomeyho/fe5bb33238d10fe6dc3182c3b37f7c1a to your computer and use it in GitHub Desktop.
Immich: remove duplicates between two users
const IMMICH_URL = "http://192.168.1.54:30041/api";
const USER_A = ""; //
const USER_A_API_KEY = "";
const USER_B = "";
const USER_B_API_KEY = "";
async function fetchAssets(userId, apiKey) {
const assets = [];
let page = "1";
while (page) {
const body = {
userId,
page,
size: 250,
};
const res = await fetch(`${IMMICH_URL}/search/metadata`, {
method: "POST",
headers: {
"x-api-key": apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
const err = await res.text();
throw new Error(
`Failed fetch metadata for ${userId}: ${res.status} ${res.statusText} — ${err}`
);
}
const data = await res.json();
if (!data?.assets?.items?.length) break;
assets.push(...data.assets.items);
page = data.assets.nextPage; // null if done
}
return assets;
}
async function deleteAssets(userId, apiKey, assetIds) {
const res = await fetch(`${IMMICH_URL}/assets`, {
method: "DELETE",
headers: {
"x-api-key": apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
force: false,
ids: assetIds
}),
});
if (!res.ok) {
const err = await res.text();
throw new Error(
`Failed delete assets for ${userId}: ${res.status} ${res.statusText} — ${err}`
);
}
}
(async () => {
const [assetsA, assetsB] = await Promise.all([
fetchAssets(USER_A, USER_A_API_KEY),
fetchAssets(USER_B, USER_B_API_KEY),
]);
console.log(`User A: ${assetsA.length} assets, User B: ${assetsB.length} assets`);
// Index by checksum for duplicates
const mapB = new Map(
assetsB.map(a => [a.checksum, a]).filter(([k]) => k)
);
const duplicates = assetsA
.filter(a => a.checksum && mapB.has(a.checksum))
.map(a => ({
userA: a,
userB: mapB.get(a.checksum),
}));
console.log(`Found ${duplicates.length} duplicates: ${
duplicates.map(d => d.userB.id)
}`);
await deleteAssets(USER_B, USER_B_API_KEY, duplicates.map(d => d.userB.id))
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment