Help us improve
Share bugs, ideas, or general feedback.
From immich-photo-manager
Analyzes disk usage in an Immich photo library and identifies storage reclamation opportunities: redundant RAW+JPEG pairs, oversized videos, bloated sidecar files, and format inefficiencies.
npx claudepluginhub drolosoft/immich-photo-manager --plugin immich-photo-managerHow this skill is triggered — by the user, by Claude, or both
Slash command
/immich-photo-manager:storage-optimizerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Before doing ANYTHING else in this skill, call `ping` on the Immich MCP server.**
Runs a comprehensive health check on an Immich photo library — asset counts, storage usage, metadata completeness, orphaned files, and quality indicators.
Automates Cloudinary media management via natural language: create folders, configure upload presets, look up assets, manage transformations, and monitor usage.
Designs and plans media pipelines for images, video, and downloadable assets, covering format selection (WebP, AVIF), responsive images, video hosting, and DAM organization.
Share bugs, ideas, or general feedback.
Before doing ANYTHING else in this skill, call ping on the Immich MCP server.
ping succeeds → proceed with the skill normally.ping fails or the MCP tools are not available → STOP. Do not continue. Tell the user:❌ Immich is not connected. This plugin needs a running Immich MCP server to work.
Run /setup-immich-photo-manager to configure your Immich connection. You'll need:
- Your Immich server URL (e.g.,
http://192.168.1.100:2283)- An Immich API key (how to create one)
- The MCP server configured (see /setup-immich-photo-manager)
Nothing in this plugin will work until the connection is configured.
Do NOT skip this check. Do NOT try to run any other tool first. Always ping, always block if it fails.
Analyze storage usage in an Immich photo library, identify the biggest consumers, and recommend strategies to reclaim space without losing important content.
-- Total storage by asset type
SELECT type,
count(*) as files,
pg_size_pretty(sum(("exifInfo"->>'fileSizeInByte')::bigint)) as total_size,
pg_size_pretty(avg(("exifInfo"->>'fileSizeInByte')::bigint)) as avg_size,
pg_size_pretty(max(("exifInfo"->>'fileSizeInByte')::bigint)) as max_size
FROM asset WHERE "deletedAt" IS NULL
GROUP BY type;
-- Trash storage (reclaimable immediately)
SELECT
count(*) as trashed_files,
pg_size_pretty(sum(("exifInfo"->>'fileSizeInByte')::bigint)) as trash_size
FROM asset WHERE "deletedAt" IS NOT NULL;
-- File formats by count and size
SELECT
upper(split_part("originalPath", '.', -1)) as format,
count(*) as files,
pg_size_pretty(sum(("exifInfo"->>'fileSizeInByte')::bigint)) as total_size,
pg_size_pretty(avg(("exifInfo"->>'fileSizeInByte')::bigint)) as avg_size,
round(100.0 * sum(("exifInfo"->>'fileSizeInByte')::bigint) /
(SELECT sum(("exifInfo"->>'fileSizeInByte')::bigint) FROM asset WHERE "deletedAt" IS NULL), 1) as pct_of_total
FROM asset WHERE "deletedAt" IS NULL
GROUP BY format ORDER BY sum(("exifInfo"->>'fileSizeInByte')::bigint) DESC;
Large files (>50MB):
SELECT "id", "originalPath", type,
pg_size_pretty(("exifInfo"->>'fileSizeInByte')::bigint) as size,
"exifInfo"->>'make' as camera,
"localDateTime"
FROM asset
WHERE "deletedAt" IS NULL
AND ("exifInfo"->>'fileSizeInByte')::bigint > 52428800 -- 50MB
ORDER BY ("exifInfo"->>'fileSizeInByte')::bigint DESC
LIMIT 50;
RAW+JPEG pairs:
-- Find RAW files that have a matching JPEG
WITH raws AS (
SELECT "originalFileName",
regexp_replace("originalFileName", '\.[^.]+$', '') as base_name,
("exifInfo"->>'fileSizeInByte')::bigint as size
FROM asset
WHERE "deletedAt" IS NULL
AND upper(split_part("originalPath", '.', -1)) IN ('ARW', 'CR2', 'CR3', 'NEF', 'RAF', 'DNG', 'ORF', 'RW2')
),
jpegs AS (
SELECT regexp_replace("originalFileName", '\.[^.]+$', '') as base_name
FROM asset
WHERE "deletedAt" IS NULL
AND upper(split_part("originalPath", '.', -1)) IN ('JPG', 'JPEG')
)
SELECT count(*) as raw_with_jpeg,
pg_size_pretty(sum(r.size)) as raw_size_reclaimable
FROM raws r
INNER JOIN jpegs j ON r.base_name = j.base_name;
Videos by size tier:
SELECT
CASE
WHEN ("exifInfo"->>'fileSizeInByte')::bigint > 1073741824 THEN '>1GB'
WHEN ("exifInfo"->>'fileSizeInByte')::bigint > 524288000 THEN '500MB-1GB'
WHEN ("exifInfo"->>'fileSizeInByte')::bigint > 104857600 THEN '100-500MB'
WHEN ("exifInfo"->>'fileSizeInByte')::bigint > 52428800 THEN '50-100MB'
ELSE '<50MB'
END as size_tier,
count(*) as videos,
pg_size_pretty(sum(("exifInfo"->>'fileSizeInByte')::bigint)) as total
FROM asset
WHERE "deletedAt" IS NULL AND type = 'VIDEO'
GROUP BY 1 ORDER BY min(("exifInfo"->>'fileSizeInByte')::bigint) DESC;
-- Monthly storage growth
SELECT
date_trunc('month', "localDateTime") as month,
count(*) as new_assets,
pg_size_pretty(sum(("exifInfo"->>'fileSizeInByte')::bigint)) as monthly_growth
FROM asset WHERE "deletedAt" IS NULL
AND "localDateTime" > now() - interval '12 months'
GROUP BY 1 ORDER BY 1;
Calculate: at current growth rate, how many months until disk is full?
STORAGE ANALYSIS
═══════════════════════════════════════
OVERVIEW
Total storage: 182.4 GB
Photos: 94.2 GB (39,596 files, avg 2.4 MB)
Videos: 85.1 GB (4,983 files, avg 17.1 MB)
Trash: 12.3 GB (8,433 files) ← RECLAIMABLE NOW
TOP CONSUMERS
1. Videos >100MB: 43 files, 18.2 GB
2. RAW files: 892 files, 22.4 GB (614 have matching JPEGs)
3. HEIC photos: 7,890 files, 31.2 GB
4. Large screenshots: 234 files, 1.8 GB
RECLAIMABLE SPACE
Empty trash: 12.3 GB
Remove RAW where JPEG exists: 15.8 GB (614 RAW files)
Remove large screenshots: 1.8 GB (234 files)
TOTAL POTENTIAL: 29.9 GB (16.4% of library)
GROWTH RATE
Last 12 months: 2.1 GB/month average
At current rate: ~24 months until 250 GB disk full
RECOMMENDATIONS
1. Empty trash immediately → 12.3 GB freed
2. Review RAW+JPEG pairs → keep JPEGs, remove RAWs for 15.8 GB
3. Review 43 videos >100MB — any worth compressing?
4. 234 large screenshots — worth keeping?
force: trueNEVER auto-delete. Always present findings and wait for approval.