Skip to main content

Photo cataloguer and metadata assistant.

Project description

Madify

CI Release Python 3.12+ License: MIT Coverage uv Ruff

Madify is a local CLI that catalogues photo, PSD, and video files in SQLite, lets you set titles/descriptions/tags, and renames files from those titles. On tag, it also writes an XMP sidecar next to the media file (catalog stays source of truth). The core never touches the filesystem, database, or clock directly — those are injected ports so unit tests stay deterministic.

Features

  • Recursive scan into SQLite (images, Photoshop .psd/.psb, common video)
  • Tag by id or path — tags merge by default; --replace-tags replaces the set
  • XMP sidecar write-back on tag (--no-sidecar to skip)
  • List / search the catalog
  • Rename from sanitized titles (Clip OneClip_One.jpg), collisions → _2, _3, …
  • Zero runtime dependencies (stdlib only)
  • Injectable Clock / FileSystem / CatalogStore / MetadataWriter ports

Install

Requires Python ≥ 3.12 and uv.

git clone https://github.com/scottmills306/Madify.git
cd Madify
uv sync --group dev
uv run madify
Madify 0.2.0

After PyPI trusted publishing is configured (see below):

uv tool install madify
# or: pip install madify

Quickstart

Keep the catalog outside the media folder so the .sqlite file is not skipped as unsupported media.

# PowerShell demo layout
$demo = Join-Path $env:TEMP "madify-demo"
$dbdir = Join-Path $env:TEMP "madify-db"
New-Item -ItemType Directory -Path $demo, $dbdir -Force | Out-Null
Set-Content "$demo\a.jpg" "x"
Set-Content "$demo\b.psd" "x"
Set-Content "$demo\c.mp4" "x"
Set-Content "$demo\readme.txt" "skip"
$db = Join-Path $dbdir "catalog.sqlite"
uv run madify --db $db scan $demo
uv run madify --db $db tag --id 1 --title "Clip One" --tag demo --tag photo
uv run madify --db $db list
uv run madify --db $db search --query clip
uv run madify --db $db rename --id 1

Commands

Command Purpose
madify scan <dir> Upsert supported media under <dir>
madify tag --id N | --path P Set title/description/tags (+ XMP sidecar)
madify rename [--id N] Rename from titles
madify list List all assets
madify search [--query Q] [--tag T] Filter assets

Global: --db PATH (default madify.sqlite).

Architecture

Module Role
cli.py Argparse wiring
models.py / errors.py Domain types and errors
media_kinds.py Extension → image / PSD / video
tagging.py Normalize/validate; tag merge/replace
naming.py Title → filename + collisions
ports.py Clock / FS / Catalog / MetadataWriter protocols
scan.py / tag_asset.py / rename_assets.py Use cases
query.py List / search filters
xmp_sidecar.py XMP sidecar writer
sqlite_catalog.py / local_fs.py / system_clock.py Adapters

Development

uv sync --group dev
uv run pre-commit install
uv build
uv run ruff check .
uv run ruff format --check .
uv run pytest -q --cov=madify --cov-report=term-missing
uv run pdoc madify -d google -o docs/api

API HTML lands in docs/api/ (gitignored).

See CHANGELOG.md, SECURITY.md, and CODE_OF_CONDUCT.md.

PyPI trusted publishing (one-time, manual)

  1. Create a PyPI project named madify (or claim the name).
  2. On PyPI → Publishing → add a trusted publisher:
    • Owner: scottmills306
    • Repository: Madify
    • Workflow: publish.yml
    • Environment: pypi
  3. On GitHub → Settings → Environments → create pypi (optional protection rules).
  4. Publish a GitHub Release — the Publish workflow uploads wheels via OIDC (no API token).

License

MIT — see LICENSE. Copyright (c) 2026 Scott Mills.

Appendix: Supported extensions

Classification is extension-only (case-insensitive). Anything else is skipped on scan.

Kind Extensions
Image .jpg, .jpeg, .png, .gif, .webp, .tif, .tiff, .bmp, .heic, .heif, .raw, .cr2, .nef, .arw, .dng, .orf, .rw2
Photoshop .psd, .psb
Video .mp4, .mov, .mkv, .avi, .webm, .m4v, .wmv, .mpg, .mpeg, .3gp

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

madify-0.2.0.tar.gz (16.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

madify-0.2.0-py3-none-any.whl (23.5 kB view details)

Uploaded Python 3

File details

Details for the file madify-0.2.0.tar.gz.

File metadata

  • Download URL: madify-0.2.0.tar.gz
  • Upload date:
  • Size: 16.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for madify-0.2.0.tar.gz
Algorithm Hash digest
SHA256 5f9c6b58832670c309bc54b2d0e0941614477b1c624722f3072e6542a5d83b37
MD5 297393d92b9f729d2cf5247db373b9c9
BLAKE2b-256 561e8f1d1e9357413d895569ae1005f810901b3b0b93e046473a306f682f048d

See more details on using hashes here.

Provenance

The following attestation bundles were made for madify-0.2.0.tar.gz:

Publisher: publish.yml on sgm-audio/Madify

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file madify-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: madify-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 23.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for madify-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f7805fa5ae44477845c40063286dfc8651d69c25a8fcedb14f7f8df3def4d4e1
MD5 03f67d9fb8b005ffb4278afcd6f5c859
BLAKE2b-256 b05170b0737fae5160a4608ddbd5f34d64bb977872be77dd11d7e154e3606314

See more details on using hashes here.

Provenance

The following attestation bundles were made for madify-0.2.0-py3-none-any.whl:

Publisher: publish.yml on sgm-audio/Madify

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page