From photography-workflow
Workflow for processing JPEG photos: view each image, generate a descriptive title (5–9 words), 60–90 word description, and 8–12 keywords; reverse-geocode any embedded GPS coordinates via Nominatim to add specific location detail; embed all metadata as IPTC fields using exiftool (without creating backup files); and rename each file to a hyphenated version of its title. USE when the user says things like: "tag these photos", "add metadata to my images", "generate keywords for photos", "embed IPTC data", "rename photos with descriptive names", "process these JPEGs", "add titles and descriptions to my photos", "geolocate my photos", or any request involving generating and embedding photo metadata at scale. Trigger even if only one of these steps is mentioned — the full workflow adds value every time.
How this skill is triggered — by the user, by Claude, or both
Slash command
/photography-workflow:photo-metadata-helperThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
End-to-end workflow for generating rich IPTC metadata for a folder of JPEGs and renaming them.
End-to-end workflow for generating rich IPTC metadata for a folder of JPEGs and renaming them.
For any new photos being considered for addition to a commercial-photography site, this skill is step 1 of 5:
Do not skip steps or change the order. Quality review runs before legal review because failing photos shouldn't consume rights-clearance effort. Property review runs before model review because property/trademark concerns are usually dispositive of the sale decision regardless of model release status. Publish is segmented out as its own step so a failed publish never silently blocks a still-clean rights audit.
Check that exiftool is installed before starting:
which exiftool && exiftool -ver
If missing: brew install exiftool (macOS) or apt install libimage-exiftool-perl (Linux).
Before reading any image, downscale it to a maximum of 1024 pixels on the long edge:
# Requires ImageMagick — check first: which magick || which convert
magick "$FILE" -resize "1024x1024>" "/tmp/preview_$(basename "$FILE")"
If the resize command fails (ImageMagick not installed, unsupported format, permission error, etc.), stop immediately and ask the user how to proceed — do not fall back to reading the original full-sized file. Use AskUserQuestion with the specific error and options such as installing ImageMagick, skipping the affected file, or aborting the batch.
Read the downscaled file from /tmp/ using the Read tool, not the original. This keeps vision payloads small and avoids read failures on large RAW or high-res files. Work in parallel batches of 6–8 at a time. For each photo, mentally note:
Don't generate metadata yet — just observe. For large sets (20+ photos) a quick first-pass scan before writing helps catch misidentifications early (a fern crozier is not a caterpillar; a Bird of Paradise flower looks alien up close).
Extract GPS from all files in one command:
exiftool -p '$FileName | $GPSLatitude $GPSLatitudeRef | $GPSLongitude $GPSLongitudeRef' /path/to/dir/*.jpg 2>/dev/null
If GPS is present, convert DMS to decimal and query Nominatim. Group photos by GPS cluster (photos taken at the same venue will cluster tightly — one lookup per cluster is enough):
curl -s "https://nominatim.openstreetmap.org/reverse?format=json&lat=LAT&lon=LON&zoom=18&addressdetails=1" \
-H "User-Agent: photo-metadata-tagger/1.0"
Parse the display_name and address fields for the specific venue, street, neighborhood, and city. Use the most specific available name (e.g., "Hortus Botanicus Amsterdam, Drie-klimaten-kas" rather than just "Amsterdam"). This location context goes into titles, descriptions, and keywords.
If no GPS is present, skip this step and use contextual clues from the images themselves.
For each photo, produce:
Title (5–9 words)
Description (60–90 words)
Keywords (8–12 tags)
Don't run 21 individual exiftool commands interactively. Write a single bash script, then execute it.
Critical flags:
-overwrite_original — this prevents exiftool from creating .jpg_original backup files-IPTC:Keywords= as the first keyword argument in each call — otherwise new keywords are appended to old onesPattern for each photo block:
Always sync ALL four title fields. 01-extract.ts / 01b-merge-new.ts read Title (XMP-dc:Title) BEFORE ObjectName, so a stale Lightroom title in Title will win over the fresh one in ObjectName and the catalog gets the truncated description.
T="Your Title Here"
D="Your 60-90 word description here."
exiftool -overwrite_original -P \
-IPTC:ObjectName="$T" \
-Title="$T" \
-XMP-dc:Title="$T" \
-IPTC:Headline="$T" \
-IPTC:Caption-Abstract="$D" \
-IPTC:Keywords= \
-IPTC:Keywords="keyword1" \
-IPTC:Keywords="keyword2" \
-IPTC:Keywords="keyword3" \
"/path/to/Original-Filename.jpg"
mv "/path/to/Original-Filename.jpg" "/path/to/${T// /-}.jpg"
The ${T// /-} bash substitution replaces all spaces with hyphens for the filename.
Script structure tip: Use a helper function to keep the script DRY:
tag_and_rename() {
local OLD_FILE="$1" NEW_TITLE="$2" DESC="$3"
shift 3
local KW_ARGS=()
for kw in "$@"; do KW_ARGS+=(-IPTC:Keywords="$kw"); done
exiftool -overwrite_original -P \
-IPTC:ObjectName="$NEW_TITLE" \
-Title="$NEW_TITLE" \
-XMP-dc:Title="$NEW_TITLE" \
-IPTC:Headline="$NEW_TITLE" \
-IPTC:Caption-Abstract="$DESC" \
-IPTC:Keywords= \
"${KW_ARGS[@]}" \
"$OLD_FILE"
mv "$OLD_FILE" "${OLD_FILE%/*}/${NEW_TITLE// /-}.jpg"
}
If any image contains an identifiable person, embed the subject's full name into the photo's metadata. This is the IPTC Extension standard for naming people in an image; downstream tools, model-release audits, and image asset managers read it.
Use the IPTC4xmpExt PersonInImage tag — written as XMP-iptcExt:PersonInImage in exiftool — plus an IPTC keyword for searchability:
exiftool -overwrite_original -P \
-XMP-iptcExt:PersonInImage="Subject Full Name" \
-IPTC:Keywords+="Subject Full Name" \
/path/to/photo.jpg
Critical rules:
-P preserves file modification time. -overwrite_original prevents .jpg_original backups.IPTC:Keywords+= (note the +=) appends; bare = would overwrite existing keywords.When to embed: every time a person is identifiable. Skip macros, architecture-only frames, distant unidentifiable figures, and silhouettes.
Verify:
exiftool -G -s -PersonInImage -Keywords /path/to/photo.jpg
Output should show [XMP] PersonInImage : <Name> and <Name> listed in [IPTC] Keywords.
After the script runs:
# Check new filenames, no backups, correct count
ls /path/to/dir/*.jpg | grep -v Plans
ls /path/to/dir/ | grep "_original" | wc -l # should be 0
# Spot-check metadata on 1-2 files
exiftool -IPTC:ObjectName -IPTC:Caption-Abstract -IPTC:Keywords /path/to/file.jpg
Confirm the title, description word count, and keyword list all look correct. Clean up /tmp/preview_* files after verification.
| Pitfall | Fix |
|---|---|
| Reading full-res originals directly | Downscale to ≤1024px long edge first with magick; read the /tmp/preview_* copy |
.jpg_original backup files created | Add -overwrite_original to every exiftool call |
| New keywords appended to old ones | Include -IPTC:Keywords= (blank) before setting new keywords |
| Misidentified subject (crozier vs caterpillar, etc.) | Review ambiguous shots carefully before writing — when uncertain, describe what you see literally |
| GPS lookup returns neighborhood not venue | Use zoom=18 in Nominatim URL for maximum specificity |
| Very long filenames from long titles | Keep titles to 5–9 words; prioritize subject over adjectives |
| Description word count outside 60–90 | Count words after drafting; trim or expand before embedding |
npx claudepluginhub derekslinz/photography-workflow --plugin photography-workflowProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.