Documentation

La ligne de commande obscura (utilisateurs avancés)

Pour utilisateurs avancés : réglages, revue, plan, exécution, reprise, renommage par lots.

Obscura Trace embarque un outil en ligne de commande, obscura, qui s’appuie sur les mêmes moteurs et le même catalogue que l’application. Il sert à automatiser, à vérifier, et à accéder à deux fonctions que l’application n’expose pas encore : le renommage par lots et les modèles de chemin lors d’un transfert.

Ce chapitre suppose que vous êtes à l’aise avec le Terminal. Aucune commande de ce chapitre n’est nécessaire pour utiliser l’application.

Où se trouve l’outil

Dans une version packagée, l’outil est dans le paquet de l’application :

"/Applications/Obscura Trace.app/Contents/Helpers/obscura" version

Pour alléger les exemples, définissez deux variables :

OBSCURA="/Applications/Obscura Trace.app/Contents/Helpers/obscura"
CATALOG="$HOME/Library/Application Support/Obscura Trace/catalog.sqlite"

"$OBSCURA" sans argument (ou avec un argument inconnu) affiche l’aide complète et se termine avec le code 2.

"$OBSCURA" version affiche la version de l’application qui contient l’outil (par exemple 0.1.1) ; c’est aussi celle qu’il inscrit dans les manifestes. Un outil copié hors de l’application affiche 0.0.0-dev.

Codes de sortie

CodeSens
0Succès.
1Erreur : l’opération n’a pas abouti (catalogue illisible, identifiant inconnu, commande JSON mal formée), ou une décision de revue ou un réglage refusé.
2Usage : commande, option ou valeur inconnue. Rien n’est ouvert.
3Refus ou interruption d’une opération sur les fichiers : plan refusé (REPLACE sans consentement, capacité insuffisante, destination absente), aperçu de renommage rejeté, disque plein ou démonté pendant l’exécution. Ce qui a été fait est consigné au journal ; la reprise s’en charge.
75Le catalogue est ouvert par un autre processus (voir ci-dessous).

Une erreur s’imprime toujours sur la sortie d’erreur, précédée de « erreur : » ou de « refusé : ». Une valeur d’option inconnue est nommée, avec les valeurs admises : --strategy skip est refusé (« valeur inconnue pour –strategy : « skip » (valeurs admises : SKIP, RENAME, COMPARE, REPLACE) »), il n’est pas compris comme COMPARE.

Précautions

  • Quittez l’application avant toute commande qui ouvre son catalogue. Un seul processus écrit dans un catalogue : tant que l’application l’a ouvert, toute commande obscura sur ce catalogue — lecture comprise (settings get, review counts, job list) — est refusée, sans rien ouvrir ni modifier, avec le code de sortie 75 :

    « refusé : CATALOG_LOCKED : le catalogue « … » est déjà ouvert par le processus N (l’application ou la CLI). Un seul processus écrit dans un catalogue : fermez l’autre, puis recommencez. Rien n’a été ouvert ni modifié. »

    Le même refus vaut entre deux commandes obscura lancées en même temps sur un même catalogue, et pour une seconde fenêtre de l’application (open -n), qui affiche « Le catalogue est déjà ouvert ». C’est ce qui empêche une commande de prendre pour interrompus les travaux en cours de l’application.

  • Faites une copie de sauvegarde du catalogue avant d’expérimenter (voir chapitre 11).

  • Chaque commande prend le catalogue explicitement par --catalog. Rien n’est lu ailleurs.

  • Commandes de mesure, à ne pas pointer vers votre catalogue : tree, memory, hash, similarity, scan et job run servent aux mesures de performance et aux tests. scan --root déclare notamment une source de mesure fictive dans le catalogue donné. Utilisez-les, si besoin, avec un catalogue jetable (--catalog /tmp/essai.sqlite). Pour analyser vos fonds, utilisez l’Assistant de scan de l’application.

  • La CLI de production ne contient aucun outil de test : toute option réservée au harnais de pannes (--crash, --trash-dir, crash…) est refusée avant d’ouvrir quoi que ce soit : « refusé : … appartient au harnais de pannes (obscura-harness), absent de la CLI de production … Rien n’a été ouvert ni modifié. »

Réglages : obscura settings

Les mêmes réglages que la fenêtre Réglages, dans le même catalogue.

"$OBSCURA" settings get --catalog "$CATALOG"
# permanentDeletionEnabled=false

Désactiver la suppression définitive :

"$OBSCURA" settings set --catalog "$CATALOG" --permanent-deletion off

L’activer exige une confirmation explicite, le drapeau --confirm-irreversible (l’équivalent de la boîte de dialogue de l’application) :

"$OBSCURA" settings set --catalog "$CATALOG" --permanent-deletion on
# refusé : une suppression définitive ne passe pas par la Corbeille et ne se récupère pas.
# Relancez avec --confirm-irreversible pour l'autoriser ; chaque job devra encore y consentir.
# permanentDeletionEnabled=false          (code de sortie 1)

"$OBSCURA" settings set --catalog "$CATALOG" --permanent-deletion on --confirm-irreversible
# permanentDeletionEnabled=true

La valeur imprimée est toujours relue dans le catalogue, jamais celle demandée. Le changement apparaît dans l’écran Activité et dans la fenêtre Réglages à sa prochaine ouverture.

Revue : obscura review

"$OBSCURA" review counts --catalog "$CATALOG"
# EXACT_DUPLICATES 20000
# PROBABLE_DUPLICATES 312
# …

"$OBSCURA" review list --catalog "$CATALOG" --filter UNRESOLVED --offset 0 --limit 20
# GROUP <groupId> EXACT_DUPLICATES UNDECIDED members=3 subject=<assetId>,<assetId>,<assetId>
# FILE <id> <chemin relatif>

Filtres : EXACT_DUPLICATES, PROBABLE_DUPLICATES, VISUAL_MATCHES, UNIQUE_ASSETS, CORRUPT_FILES, UNRESOLVED (par défaut), NEEDS_REVIEW — les filtres du Centre de revue.

Enregistrer une décision, avec la même commande JSON que celle qu’émet l’application :

"$OBSCURA" review decide --catalog "$CATALOG" \
  --command '{"action":"CHOOSE_MASTER","subjectKey":"<a>,<b>,<c>","assetId":"<b>"}'
# {"decisionId":"…","status":"IN_PROGRESS"}
  • subjectKey : la valeur subject= de review list (les identifiants des membres, séparés par des virgules).
  • action : KEEP, MARK_DUPLICATE, MARK_VERSION, CHOOSE_MASTER (avec assetId, l’un des membres), ou IGNORE, REOPEN (sans assetId).
  • Un refus s’imprime sur la sortie d’erreur (« refusé : … ») avec le code 1 ; rien n’est enregistré.

Aucune décision ne touche un fichier.

Déplacements proposés (T-29) — les lister, puis les décider :

"$OBSCURA" review proposals --catalog "$CATALOG"
# PROPOSAL <proposalId> PENDING lost=<assetId> new=<assetId> 2019/a.jpg -> Archives 2019/a.jpg

"$OBSCURA" review settle --catalog "$CATALOG" \
  --command '{"proposalId":"<proposalId>","decision":"ACCEPT"}'
# {"assetId":"…","proposalId":"…","status":"ACCEPTED"}

decision : ACCEPT (rattacher, définitif) ou REJECT (garder séparés). Une proposition déjà décidée ou périmée est refusée (« refusé : … », code 1), sans rien écrire.

Option « empreinte complète » d’une source :

"$OBSCURA" settings fingerprint --catalog "$CATALOG" --source <sourceId> --full on
# fullFingerprint=true

Transferts : plan, execute, recover-transfers

Il vous faut l’identifiant de la source (source-…). La CLI n’a pas de commande pour lister les sources ; vous pouvez le lire, en lecture seule, avec l’outil sqlite3 de macOS :

sqlite3 -readonly "$CATALOG" "SELECT sourceId, displayName FROM sources;"

Planifier (ne transfère rien)

"$OBSCURA" plan --catalog "$CATALOG" --source-id <sourceId> --destination /Volumes/Archive \
  [--move] [--strategy COMPARE|SKIP|RENAME] [--template '<modèle de chemin>'] [--dry-run]
# plan=<planId> fingerprint=<empreinte> operations=<n> bytes=<octets>
  • La racine de la source est retrouvée par son signet, jamais par un chemin saisi.
  • --strategy vaut COMPARE par défaut. La CLI ne transmet pas de consentement au remplacement : REPLACE y est refusé.
  • --template range les fichiers selon un modèle de chemin, par exemple '{YEAR}/{MONTH}/{ORIGINAL_NAME}' (variables ci-dessous). Sans lui, l’arborescence de la source est conservée.
  • --move planifie un déplacement ; voir la réserve sur la Corbeille plus bas.
  • La suppression définitive n’est pas accessible par plan.

Exécuter un plan réel

"$OBSCURA" execute --catalog "$CATALOG" --plan <planId> --source-id <sourceId> \
  --executor copy-first [--manifest]
# performed=… alreadyVerified=… failed=… trashed=0 …
# manifest=/Volumes/Archive/.obscura-manifests/<planId>/manifest.json
  • --executor copy-first est obligatoire dans la CLI de production : c’est l’exécutant du produit (copie, vérification, promotion).
  • --manifest écrit le manifeste avant la fin du job.
  • La CLI ne touche jamais la Corbeille. Pour un déplacement exécuté par la CLI, chaque source reste en place, comme sur un disque sans Corbeille : c’est une copie vérifiée, et le retrait des sources vous revient.
  • Aucun écran de confirmation dans la CLI : lire le plan avant de l’exécuter vous incombe. Relancer execute sur le même plan ne recopie pas ce qui est déjà vérifié.

Reprise après interruption

"$OBSCURA" recover-transfers --catalog "$CATALOG" --executor copy-first
# dangling=<n> verifiedInPlace=<n> rolledBack=<n>

La reprise lit uniquement le journal : pour chaque opération restée ouverte, elle vérifie la copie en place ou la défait. Relancez ensuite execute sur le même plan pour terminer le reste.

Renommage par lots : rename, recover-renames

Le renommage par lots n’est pas disponible dans l’application ; il l’est dans la CLI. Il renomme les fichiers en place, dans leur dossier, selon un modèle de nom.

  1. Aperçu (ne renomme rien) :

    "$OBSCURA" rename --catalog "$CATALOG" --source-id <sourceId> \
      --source-root "/Volumes/Photos" --template '{YYYY}-{MM}-{DD}_{ORIGINAL_NAME}'
    # PREVIEW 2004/IMG_0042.CR2 -> 2004/2004-07-14_IMG_0042.CR2
    # ISSUE …
    # pairs=… issues=… skipped=… fingerprint=<empreinte>
  2. Application, en exigeant que l’aperçu soit exactement celui que vous avez lu :

    "$OBSCURA" rename … --apply --fingerprint <empreinte>
    # job=<jobId> renamed=… failed=…

    Si l’aperçu a changé entre-temps : « REFUSED preview différente de celle annoncée » (code 3), rien n’est renommé.

  3. Annulation d’un lot : --undo <jobId> à la place de --template produit l’aperçu de la compensation ; ajoutez --apply pour l’appliquer.

  4. Reprise après une interruption : "$OBSCURA" recover-renames --catalog "$CATALOG".

Règles :

  • le modèle est un nom, sans « / » ; l’extension d’origine est conservée telle quelle (.TIF reste .TIF) ;
  • une valeur absente est remplacée par un jeton fixe, par exemple unknown-camera ;
  • --source-root doit être la racine actuelle de la source (celle affichée dans l’Assistant de scan) ;
  • les compagnons suivent leur fichier principal ;
  • aucun renommage sur un disque exFAT ou FAT32 : ces formats ne garantissent pas le renommage sans écrasement ; l’aperçu l’annonce par une ligne REFUSED_VOLUME, l’opération échoue et aucun nom ne change.

Variables des modèles

{DATE}, {YEAR} (ou {YYYY}), {MONTH} (ou {MM}), {DAY} (ou {DD}), {CAMERA}, {LENS}, {ISO}, {LOCATION}, {CATEGORY}, {ORIGINAL_NAME}, {SEQUENCE}, {HASH8}. L’application n’a ni géolocalisation ni classification : {LOCATION} et {CATEGORY} reçoivent leur jeton d’absence.

Jobs : obscura job

"$OBSCURA" job list --catalog "$CATALOG"
# <jobId> COMPLETED 1284234/1284234
# <jobId> RUNNING 5120/-

"$OBSCURA" job recover --catalog "$CATALOG"
# recovered=<n> ids=…

job list montre l’état de chaque job (analyses, plans, transferts, renommages). job recover rejoue la reprise de démarrage : les jobs restés « en cours » après l’arrêt brutal d’un processus sont marqués interrompus.