FrameTags - Batch metadata editor for photographers.
FrameTags is a PySide6 desktop utility for applying metadata to large photo sets in bulk without Lightroom or command-line workflows.
Photographers who need repeatable metadata workflows across export folders, trip folders, and archives.
- Select one or more root directories
- Scan with optional recursive traversal
- Select exactly which metadata fields to apply
- Preview real per-file change sets before writing
- Apply metadata writes through ExifTool
- Save, load, update, and delete reusable presets
This tool does not edit image pixels. It only reads and writes metadata.
- RAW:
.cr2,.nef,.arw,.dng,.orf,.raf - Standard image:
.jpg,.jpeg,.tif,.tiff,.png
PNG metadata support depends on ExifTool capabilities and file metadata blocks.
- Artist
- Copyright
- Credit
- Source
- Creator Email
- Creator Website
- Usage Terms / License
- Location Name
- City
- State / Province
- Country
- GPS Latitude
- GPS Longitude
- Caption / Description
- Headline
- Keywords
Keywords accept comma-separated input and are handled as distinct values.
FrameTags separates UI fields from raw metadata tags using a registry in app/metadata_fields.py.
- UI controls bind to normalized internal keys (for example
artist,caption,keywords) - Mapping to Exif/IPTC/XMP tag targets is centralized in field definitions and
app/metadata_mapper.py - Preview and apply share the same change-set datamodel (
FileChangeSet,FileChangeAction)
This keeps the app extensible for future inspector, normalization, reporting, and copy workflows.
overwrite: replace existing valueswrite_if_empty: write only where current value is blankappend_keywords: append non-duplicate keywords
RAW handling is strategy-based from day one:
- embedded for JPEG/TIFF
- sidecar-aware strategies for RAW formats
- user option: prefer sidecar for RAW
Presets are stored in data/presets.json.
- Save current checked non-empty field values as a named preset
- Load preset values into the form
- Update or delete existing presets
Only selected fields with values are persisted.
Preview is required before apply.
Preview pipeline:
- scan candidate files
- read existing metadata through ExifTool
- compare against selected field values and write mode
- build per-file change sets
- show only files that would actually change
Apply consumes the exact preview change-set model.
Changed Fields in preview means fields whose value would actually change on that file.
- A checked field is not counted if the existing value already matches the entered value.
- This is true even when using
overwritemode. - So if 6 fields are checked, a file may show 3/4/5 changed fields depending on what already matches.
General settings are saved in data/settings.json:
- last selected preset
- last used directories
- recurse option
- write mode
- RAW sidecar preference
- window geometry
FrameTags requires PyExifTool and an ExifTool executable. FrameTags first checks for exiftool.exe next to the app, then falls back to system PATH.
- ExifTool: https://exiftool.org/
If ExifTool is missing, preview/apply actions will be blocked with an error message.
From the frametags directory:
python -m venv .venv
# Windows
.venv\Scripts\activate
pip install -r requirements.txt
python main.pyRequires Python 3.12+.
From E:\FrameTags\frametags in cmd:
.venv\Scripts\activate
build.batbuild.bat will:
- install build dependency from
build-requirements.txt - run
PyInstallerwithframetags.spec - create output in
dist\FrameTags
Distribution layout:
dist\\FrameTags\\
FrameTags.exe
exiftool.exe
exiftool_files\\
data\\
[PyInstaller runtime files]
Zip dist\FrameTags and share it. Users can run FrameTags.exe directly.
- Metadata inspector (single-file normalized view)
- Metadata normalization scans and standardization workflows
- Metadata reporting (value distributions and keyword counts)
- RAW -> JPEG metadata copy workflows
- richer sidecar-aware workflows