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" versionPour 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
| Code | Sens |
|---|---|
| 0 | Succès. |
| 1 | Erreur : 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é. |
| 2 | Usage : commande, option ou valeur inconnue. Rien n’est ouvert. |
| 3 | Refus 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. |
| 75 | Le 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
obscurasur 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
obscuralancé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,scanetjob runservent aux mesures de performance et aux tests.scan --rootdé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=falseDésactiver la suppression définitive :
"$OBSCURA" settings set --catalog "$CATALOG" --permanent-deletion offL’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=trueLa 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 valeursubject=dereview list(les identifiants des membres, séparés par des virgules).action:KEEP,MARK_DUPLICATE,MARK_VERSION,CHOOSE_MASTER(avecassetId, l’un des membres), ouIGNORE,REOPEN(sansassetId).- 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=trueTransferts : 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.
--strategyvautCOMPAREpar défaut. La CLI ne transmet pas de consentement au remplacement :REPLACEy est refusé.--templaterange 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.--moveplanifie 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-firstest 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
executesur 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.
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>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é.
Annulation d’un lot :
--undo <jobId>à la place de--templateproduit l’aperçu de la compensation ; ajoutez--applypour l’appliquer.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 (
.TIFreste.TIF) ; - une valeur absente est remplacée par un jeton fixe, par exemple
unknown-camera; --source-rootdoit ê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.