fonzarely/regine-photos-archiverpublic⑂ Fork 0
⑂ main
Commits
⬇ Clone ▾
git clone https://git.rickub.com/fonzarely/regine-photos-archiver.git
git clone ssh://git@rickub.com/fonzarely/regine-photos-archiver.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

refactor 19cf29c · on main · Fabien Champigny · yesterday
README.md

📸 Régine

L'archiviste qui protège vos photos comme un musée protège ses négatifs.

Régine range, nomme et sécurise vos photos RAW + JPEG selon les pratiques des grandes collections professionnelles (Magnum Photos, ICP, Getty, archives nationales) — appliquées à votre NAS personnel. Vous continuez à trier et retoucher avec l'outil de votre choix (DxO, Lightroom...) ; Régine s'occupe du reste : import depuis la carte mémoire, nommage lisible, structure de dossiers cohérente, et un aller-retour sécurisé entre votre archive et votre espace de travail qui garantit qu'un fichier maître n'est jamais modifié ni perdu sans confirmation explicite.

CLI-first aujourd'hui, avec une interface graphique et un agent IA prévus comme façades sur la même bibliothèque — cf. Feuille de route.


Pourquoi Régine ?

Après quelques années de prises de vue régulières, la plupart des photothèques personnelles finissent dans le même état : des dossiers IMG_2024, Export_final_v2, Untitled Folder ; des RAW et des JPEG mélangés sans lien visible ; aucune certitude sur ce qui a déjà été trié, sauvegardé, ou modifié par erreur.

Régine part des principes qu'utilisent les collections professionnelles — organisation dès la source, métadonnées embarquées, identité par contenu plutôt que par nom de fichier, jamais d'écrasement silencieux — et les rend accessibles à un·e photographe seul·e, sans infrastructure d'archives. Voir docs/archivage-photo-elements-cles.md pour les notes de recherche complètes qui fondent ces choix.

Le cycle de vie d'une photo dans Régine

flowchart LR
    CARD(["📷 Carte mémoire"])

    subgraph IMPORT["1 · Import — specs/001"]
        direction TB
        COPY["Copie vérifiée<br/>(une seule lecture de la carte)"]
        SPLIT["Regroupement<br/>jour par jour"]
        NAME["Nommage lisible<br/>AAAA-MM-JJ_Titre_nomOrigine"]
        COPY --> SPLIT --> NAME
    end

    subgraph ARCHIVE["🗄️ Archive · NAS"]
        DOSSIER["Dossier immuable<br/>raw/ · jpeg/ · tiff/ · racine de sélection"]
    end

    subgraph LOCAL["💻 Espace de travail local"]
        direction TB
        OUT["Copie de travail<br/>(laissée par l'import, ou par un checkout — specs/005)"]
        EDIT["Tri &amp; retouche<br/>(DxO, Lightroom, votre outil…)"]
        RECON["Réconciliation<br/>par hash, avant tout réarchivage"]
        OUT --> EDIT --> RECON
    end

    CARD --> COPY
    NAME -->|archivage vérifié| DOSSIER
    NAME -.->|copie locale laissée immédiatement| OUT
    DOSSIER -->|checkout, à tout moment| OUT
    RECON -->|réarchivage vérifié, jamais silencieux| DOSSIER

Deux garanties structurent tout ce cycle :

  • Le fichier maître (RAW, TIFF de scan, ou JPEG seul) n'est jamais modifié dans l'archive. Vos retouches vivent dans des sidecars (.xmp, .dop) ou, pour le DNG, dans des métadonnées séparées des pixels — jamais dans les données image elles-mêmes.
  • Rien n'est écrit sur l'archive sans un « point avant archive » : la liste des changements détectés (normal, anomalie, renommage, suppression, nouveau fichier), présentée et validée explicitement avant toute écriture sur le NAS.

Un import, en 30 secondes

Prenons des photos prises avec un Fuji X70 le weekend du 11 septembre 2026, à Deauville :

Sur la carte Dans l'archive
R0018279.JPG 2026-09-11_Weekend_Deauville_R0018279.JPG
R0018279.RAF 2026-09-11_Weekend_Deauville_R0018279.RAF
Élément Origine
2026-09-11 Date de prise de vue, lue dans les métadonnées EXIF — jamais la date du fichier
Weekend_Deauville Titre donné par l'utilisateur au moment de l'import
R0018279 Référence d'origine du boîtier, conservée pour garder l'ordre des prises de vue

Le nom de fichier reste lisible et traçable même ouvert dans dix ans, hors de Régine. Deux boîtiers différents produisant par coïncidence le même nom d'origine ? Régine les désambiguïse automatiquement par le tag EXIF du modèle d'appareil, sans configuration préalable requise.

Ce que Régine garantit

Cinq principes non négociables, posés dans la constitution du projet :

Principe En pratique
🔒 Fichier maître intouchable RAW, TIFF de scan, JPEG seul ou jumeau RAW+JPEG : jamais modifié une fois archivé. Toute altération détectée est signalée, jamais réarchivée en silence.
✋ Confirmation explicite avant toute action destructive Aucune suppression automatique — anomalie ou déchet identifié à l'import, la décision finale revient toujours à vous.
🔗 Identité par contenu, jamais par nom de fichier seul Renommages, déplacements et doublons se détectent par hash SHA-256, jamais par chemin — un fichier reste identifiable même déplacé ou renommé.
🌍 Métadonnées ouvertes et embarquées IPTC/XMP/EXIF dans le fichier lui-même, jamais dans une base propriétaire séparée qui ne survivrait pas à un changement d'outil.
💡 L'utilisateur décide, Régine suggère Découpage d'un import, détachement d'un jour particulier, désambiguïsation de boîtiers : toujours proposé, jamais imposé d'autorité.

Vocabulaire essentiel

Terme Ce que c'est
dossier Unité créée à l'import d'une carte mémoire : structure par format (raw/, jpeg/...) + racine de sélection. L'unité de checkout, de réconciliation et de verrouillage.
sous-dossier Une étape d'un dossier multi-parties (ex. une ville d'un voyage). Ne se checkout jamais isolément — toujours via son dossier parent.
dossier parent Regroupe plusieurs sous-dossiers, avec sa propre racine servant de planche-contact globale sur l'ensemble (ex. les meilleures photos de tout le voyage).
projet Sélection composée par copie depuis un ou plusieurs dossiers de l'archive (livre, expo) — en aval, indépendant de la hiérarchie d'archivage.
répertoire racine (archive) Premier niveau sous l'archive : une année (2026/) par défaut, ou une catégorie thématique (voyage/, mariage/...) si le dossier en relève.

Glossaire complet : docs/lexique.md.

Tour des fonctionnalités

Module Ce qu'il fait Spec Statut
Import carte mémoire Copie vérifiée en une seule lecture, regroupement jour par jour, nommage lisible, désambiguïsation automatique de boîtiers specs/001 ✅ Implémenté
Profil de boîtiers Désambiguïsation multi-appareils par tag EXIF Model, en repli sur BodySerialNumber ou étiquetage manuel — optionnel, jamais un prérequis specs/002 ✅ Implémenté
Catégorisation des dossiers Placement à la racine par année ou par catégorie thématique extensible (voyage, mariage, vacances...) specs/004 ✅ Implémenté
Checkout / réconciliation Aller-retour sécurisé archive ↔ espace de travail local, manifeste persistant par dossier, classification des changements par hash specs/005 ✅ Implémenté
Configuration du contexte de travail Chemins (temp, local, NAS/SMB), aide au montage, base de travail centralisée, nommage des boîtiers specs/003 ✅ Implémenté
Interface graphique Tri/culling (regroupement RAW+JPEG, promotion à la racine) et consultation en lecture seule de l'archive avec restauration ciblée specs/006 ✅ Implémenté
Vérification périodique (scrub) Détection de corruption silencieuse (bit rot) indépendamment de tout checkout constitution 📝 Prévue
Agent IA Façade conversationnelle au-dessus de la même bibliothèque centrale docs/interface-agent-ia.md 📝 Prévue

Installation

Prérequis : uv, ExifTool (lecture/écriture des métadonnées).

git clone https://github.com/regine-photo/regine-photo.git
cd regine-photo
uv sync
uv run pytest packages/regine-core/tests/

uv sync installe aussi PySide6 (interface graphique, ~400 Mo au premier téléchargement) — nécessaire uniquement pour lancer la GUI, pas pour la CLI seule.

Utiliser Régine

La bibliothèque (regine-core) est stable et testée ; les façades CLI (regine-cli) et GUI (regine-gui) s'invoquent aujourd'hui module par module (python -m), en attendant une commande unifiée regine (cf. Feuille de route). Tous les exemples ci-dessous sont exécutables tels quels depuis la racine du dépôt.

Import simple d'une carte mémoire

uv run python -m regine_cli.import_cmd import /Volumes/CARTE_SD \
  --titre "Sortie parc" --annee --yes \
  --archive-root /Volumes/NAS/photos --local-root ~/regine/local

Régine copie chaque fichier en une seule lecture de la carte (vérification par checksum), détecte la plage de dates, et archive sous <archive>/2026/2026-08-15_Sortie_parc/. Une copie de travail locale identique est laissée sous <local-root>/2026/2026-08-15_Sortie_parc/ — vous pouvez trier/retoucher tout de suite après l'import, sans étape de checkout séparée. Le dossier reste verrouillé côté archive tant que vous n'avez pas lancé un reconcile (même « à vide », cf. ci-dessous) pour le libérer.

Un voyage en plusieurs étapes

# Première étape : crée le dossier parent (catégorie "voyage") + sa première étape
uv run python -m regine_cli.import_cmd import /Volumes/CARTE_ETAPE1 \
  --titre "Montenegro" --categorie voyage --destination parent \
  --archive-root /Volumes/NAS/photos --local-root ~/regine/local

# Étape suivante : nouveau sous-dossier du même parent, catégorie héritée automatiquement
uv run python -m regine_cli.import_cmd import /Volumes/CARTE_ETAPE2 \
  --titre "Kotor" --destination sous-dossier:voyage/2026-08_Montenegro \
  --archive-root /Volumes/NAS/photos --local-root ~/regine/local

Résultat : voyage/2026-08_Montenegro/2026-08-12_Kotor/, imbriqué sous le dossier parent — dont la racine sert de planche-contact sur l'ensemble du voyage.

Reprendre un dossier déjà archivé, en sécurité

Après un import, la copie locale existe déjà (ci-dessus) — inutile de la re-checkouter. Cette étape sert à reprendre un dossier plus tard (nouvelle session, nouvel ordinateur), ou simplement à conclure une session de tri en cours :

# Checkout : sort une copie de travail locale, avec verrou côté archive
# (uniquement si vous n'avez pas déjà de copie locale en cours pour ce dossier)
uv run python -m regine_cli.archive_cmd checkout \
  /Volumes/NAS/photos/voyage/2026-08_Montenegro/2026-08-12_Kotor \
  --local-dest ~/regine/local/2026-08-12_Kotor

# ... tri et retouche libres dans DxO, Lightroom, ou l'outil de votre choix ...

# Réconciliation : classe chaque changement par hash, affiche le "point avant archive"
# et demande confirmation avant toute écriture sur le NAS — libère aussi le verrou,
# même sans aucun changement (à lancer après un import comme après un checkout)
uv run python -m regine_cli.archive_cmd reconcile \
  /Volumes/NAS/photos/voyage/2026-08_Montenegro/2026-08-12_Kotor \
  --local-dest ~/regine/local/2026-08-12_Kotor

Lancer l'interface graphique

uv run python -m regine_gui.app

Ouvre une fenêtre à deux onglets :

  • Tri — bouton « Ouvrir un dossier… » pour sélectionner une copie de travail locale (celle laissée par un import, ou par un checkout) ; les paires RAW+JPEG jumelles apparaissent comme une seule entrée, promotion/rétrogradation à la racine en un clic. Chaque action a un équivalent CLI direct (regine dossier list/promote/demote, ci-dessous), garanti par le Principe CLI-first de la constitution — la GUI n'est qu'une façade de plus sur regine-core, jamais une capacité qui lui serait propre.
  • Consultation — recherche en lecture seule dans l'archive configurée (regine config set-paths, cf. ci-dessus) par nom de dossier, date ou titre, avec restauration ciblée d'un fichier précis vers un emplacement local sans checkout complet ni verrou.

Équivalents CLI (utiles pour scripter, ou si aucun environnement graphique n'est disponible) :

# Tri : lister, promouvoir, rétrograder
uv run python -m regine_cli.dossier_cmd dossier list ~/regine/local/2026-08-15_Sortie_parc
uv run python -m regine_cli.dossier_cmd dossier promote \
  ~/regine/local/2026-08-15_Sortie_parc \
  ~/regine/local/2026-08-15_Sortie_parc/raw/2026-08-15_Sortie_parc_R0018279.RAF

# Consultation : rechercher puis restaurer un fichier précis de l'archive configurée
uv run python -m regine_cli.archive_cmd browse --search "Montenegro"
uv run python -m regine_cli.archive_cmd restore \
  --chemin voyage/2026-08_Montenegro/2026-08-12_Kotor/raw/2026-08-12_Kotor_RD0001.RAF \
  --destination ~/Desktop/RD0001.RAF

Construire une app de bureau (macOS, Linux, Windows)

Pour distribuer regine-gui sans passer par uv run python -m, PyInstaller empaquette la GUI et ses dépendances (Python, PySide6, regine-core) en un exécutable autonome :

uv sync --group build   # installe pyinstaller (une seule fois)
uv run pyinstaller packages/regine-gui/Regine.spec --noconfirm \
  --distpath packages/regine-gui/dist --workpath packages/regine-gui/build

Le résultat apparaît dans packages/regine-gui/dist/ :

  • macOS → Regine.app, double-cliquable (non signé : au premier lancement, clic droit → Ouvrir pour passer l'avertissement Gatekeeper « développeur non identifié »).
  • Linux/Windows → un dossier Regine/ contenant l'exécutable (Regine ou Regine.exe) et ses dépendances — pas de .app, ce format étant spécifique à macOS.

Est-ce que ça marche aussi sous Linux et Windows ? PyInstaller lui-même est multiplateforme et le même Regine.spec fonctionne tel quel sur les trois OS (les instructions spécifiques à macOS, comme la création du .app, ne s'exécutent simplement pas ailleurs). Deux limites à connaître :

  • Pas de cross-compilation : PyInstaller empaquette pour l'OS sur lequel il tourne. Pour produire un exécutable Windows, il faut lancer cette commande sur une machine Windows (idem Linux) — un Mac ne peut pas fabriquer de .exe.
  • exiftool reste une dépendance externe non embarquée, sur les trois plateformes : regine-core l'invoque en sous-processus (binaire système, pas un paquet Python), donc chaque machine cible doit l'avoir installé séparément, packaging ou non.

Cette configuration n'a été construite et testée que sur macOS pour l'instant — un build Linux/Windows n'a pas encore été essayé en pratique, mais repose sur le même mécanisme.

Architecture du monorepo

Une bibliothèque centrale porte toute la logique métier ; chaque interface (CLI aujourd'hui, GUI et agent IA demain) n'est qu'une façade fine au-dessus, sans logique dupliquée — Principe VI de la constitution.

flowchart TB
    subgraph FACADES [" Façades minces — aucune logique métier "]
        direction LR
        CLI["📟 regine-cli<br/>✅ implémentée"]
        GUI["🖥️ regine-gui (PySide6)<br/>✅ implémentée"]
        AGENT["🤖 regine-agent<br/>📝 réservée"]
    end

    CORE["📚 regine-core — bibliothèque centrale<br/>metadata · config · dossier · camera_profile<br/>integrity · archive · import_carte"]

    CLI --> CORE
    GUI --> CORE
    AGENT --> CORE
regine-photos-archiver/
├── packages/
│   ├── regine-core/    # toute la logique métier — testée, sans dépendance aux façades
│   ├── regine-cli/     # façade CLI (regine import, regine checkout, regine reconcile, regine dossier, regine browse...)
│   ├── regine-gui/     # façade graphique (PySide6) : tri/culling + consultation
│   └── regine-agent/   # façade agent IA — réservée
├── docs/                # notes de conception (source de vérité du domaine)
└── specs/                # spécifications Spec-Kit, une par fonctionnalité

🗺 Feuille de route

  • Commande unifiée regine (un seul point d'entrée CLI packagé)
  • Intégration complète config ↔ import/checkout (regine import/checkout lisent encore --archive-root/--local-root en flags plutôt que le contexte configuré par regine config)
  • Vérification périodique de l'archive (scrub, indépendante du checkout)
  • Planche-contact JPEG annotée (export statique, indépendant de la GUI)
  • Vignettes/aperçus dans l'écran de tri de la GUI (actuellement une liste de noms de fichiers)
  • Agent IA conversationnel

Développement piloté par les specs

Ce projet est construit avec Spec-Kit : chaque fonctionnalité part d'une spécification validée avant d'être planifiée, découpée en tâches, puis implémentée — jamais l'inverse. Les skills sont installés dans .claude/skills et s'utilisent avec Claude Code :

  1. /speckit-constitution — établir les principes du projet
  2. /speckit-specify — créer la spécification d'une fonctionnalité
  3. /speckit-plan — créer le plan d'implémentation
  4. /speckit-tasks — générer les tâches concrètes
  5. /speckit-implement — exécuter l'implémentation
  6. /speckit-converge — évaluer le code existant et combler les tâches manquantes

Skills complémentaires

Skill Rôle
/speckit-clarify Pose des questions structurées pour lever les ambiguïtés avant la planification (avant /speckit-plan)
/speckit-analyze Rapport de cohérence et d'alignement entre les artefacts (après /speckit-tasks, avant /speckit-implement)
/speckit-checklist Génère des checklists qualité pour valider la complétude et la clarté des exigences (après /speckit-plan)