# CLAUDE.md - Contexte projet pour agent IA ## Description du projet Script Python de gestion des métadonnées EXIF pour photos argentiques. Lit un fichier JSON généré par l'application mobile **Exif Notes** et écrit les métadonnées dans les fichiers images correspondants. ## Stack technique - **Python 3.11+** (utilise `zoneinfo` natif) - **exiftool** (CLI) appelé via `subprocess` — pas de wrapper Python - **pytest** pour les tests - **flake8** pour le linting (max-line-length=100) ## Structure du projet ``` json_to_metadata/ ├── __init__.py # Version du package ├── cli.py # Point d'entrée CLI (argparse) ├── json_parser.py # Lecture et validation du JSON ├── metadata_mapper.py # Mapping JSON → tags EXIF ├── exif_writer.py # Écriture EXIF via exiftool └── xmp_writer.py # Génération de fichiers XMP sidecar tests/ ├── test_json_parser.py ├── test_metadata_mapper.py ├── test_exif_writer.py └── test_xmp_writer.py ``` ## Commandes utiles ```bash # Activer l'environnement source venv/bin/activate # Lancer le script python -m json_to_metadata -d # Options CLI # -v, --verbose Mode verbeux (DEBUG) # --dry-run Affiche sans exécuter # --force-xmp Force création XMP même pour TIFF/JPEG # --set-file-date Modifie le mtime des fichiers selon la date de prise de vue # Tests pytest tests/ -v # Linting flake8 json_to_metadata/ tests/ --max-line-length=100 ``` ## Format JSON attendu (Exif Notes) ```json { "id": "roll-001", "camera": { "make": "Nikon", "model": "FM2" }, "lens": { "make": "Nikon", "model": "Nikkor 50mm f/1.4" }, "filmStock": { "make": "Kodak", "model": "Portra 400" }, "iso": 400, "frames": [ { "id": 1, "date": "2024-03-15T14:30", "shutter": "1/125", "aperture": "f/2.8", "focalLength": 50, "location": { "latitude": 48.8584, "longitude": 2.2945 }, "note": "Description de la photo", "flashUsed": false, "lightSource": "daylight" } ] } ``` ## Mapping JSON → EXIF | Champ JSON | Tag EXIF | |------------|----------| | `camera.make/model` | `Make`, `Model` | | `lens.make/model` | `LensMake`, `LensModel` | | `iso` | `ISO` | | `frame.date` | `DateTimeOriginal`, `CreateDate` | | `frame.shutter` | `ExposureTime`, `ShutterSpeedValue` | | `frame.aperture` | `FNumber`, `ApertureValue` | | `frame.focalLength` | `FocalLength` | | `frame.location.*` | `GPSLatitude/Ref`, `GPSLongitude/Ref` | | `frame.note` | `ImageDescription`, `UserComment` | | `frame.flashUsed` | `Flash` | | `filmStock.*` | Inclus dans `ImageDescription` | ## Formats supportés ### Vitesses d'obturation - Fractions : `1/125`, `1/1000` - Secondes : `2`, `2s`, `2"`, `4'` - Mode Bulb : `B`, `bulb` ### Dates - ISO complet : `2024-03-15T14:30:00` - Sans secondes : `2024-03-15T14:30` - Avec timezone : `2024-03-15T14:30:00+02:00` - Date seule : `2024-03-15` ### Images - EXIF intégré : `.tif`, `.tiff`, `.jpg`, `.jpeg` - Support partiel (fallback XMP) : `.avif`, `.heic`, `.webp` - XMP sidecar uniquement : autres formats ## Conventions de code - Docstrings en français - Messages de log en français - Fuseau horaire par défaut : `Europe/Paris` - Pas d'emoji sauf demande explicite ## Points d'extension possibles - Ajout de nouveaux tags EXIF dans `metadata_mapper.py` (fonction `map_frame_to_exif`) - Nouveaux formats de date dans `parse_date()` (liste `formats`) - Support de nouveaux formats d'image dans `exif_writer.py` (constantes `*_EXTENSIONS`)