> [!NOTE] > I haven't used Darktable to edit photos in a long while but have continued to use the application because I have the wonkiest workflow on planet earth: > > - Insert Memory Card into Linux Tower > - Use Darktable to achieve this naming convention > > Base directory naming pattern: `/media/wm/Aperture/RAW/$(EXIF_YEAR)/$(EXIF_MONTH)/` > - Sub directory naming pattern: `$(EXIF_YEAR)-$(EXIF_MONTH)-$(EXIF_DAY)` > - File naming pattern: > > Syntax > > ``` > WP3_$(EXIF_YEAR)$(EXIF_MONTH)$(EXIF_DAY)_$(EXIF_HOUR)$(EXIF_MINUTE)$(EXIF_SECOND)_$(SEQUENCE).$(FILE_EXTENSION) > ``` > > Rendered Example > > ``` > WP3_20260413-135358_0391.JPG > ``` > > - The rest of the workflow is not relevant here. I'm curious if there was a way to achieve this by other means. The only caveat, and why I have been still using Darktable is because it recognizes the RAW+JPG pair and gives them the exact same name despite being different extensions. # 001 You’re not crazy—what you’re doing is actually *very* intentional and clean. The only thing tying you to Darktable is one specific capability: > **Pair-awareness (RAW + JPG getting identical names automatically)** Most tools can rename files from EXIF. Very few handle **paired files intelligently** out of the box. That said, you’ve got solid Linux-native options that can fully replace this. --- # 🔧 Option 1 — `exiftool` (best overall replacement) This is the closest thing to a drop-in, terminal-native version of your workflow—with **pair handling baked in**. ### Why this works - Reads EXIF timestamps reliably - Can rename multiple formats together - Applies **the same base name to RAW+JPG pairs automatically** - No database, no UI overhead --- ## 🧠 Your exact pattern translated ```bash exiftool -r \ -d "/media/wm/Aperture/RAW/%Y/%m/%Y-%m-%d/WP3_%Y%m%d-%H%M%S_%%-04c.%%e" \ "-FileName<CreateDate" \ /path/to/import ``` --- ## 🔍 Breakdown (important bits) - `-r` → recursive - `-d` → defines your full directory + filename structure - `%Y/%m/%Y-%m-%d` → matches your folder logic exactly - `WP3_%Y%m%d-%H%M%S` → your naming convention - `%%-04c` → sequence number (like your `_0391`) - `%%e` → extension (keeps RAW + JPG separate but same base) --- ## 💥 The key magic `exiftool` groups files by timestamp automatically when using: ```bash "-FileName<CreateDate" ``` So if you have: ``` IMG_1234.CR2 IMG_1234.JPG ``` They become: ``` WP3_20260413-135358_0001.CR2 WP3_20260413-135358_0001.JPG ``` 👉 Same base name. No hacks needed. --- # ⚡ Option 2 — `rapid-photo-downloader` (GUI but powerful) If you want something closer to Darktable *without being Darktable*: ## Rapid Photo Downloader - Built specifically for ingest workflows - Handles RAW+JPG pairing cleanly - GUI but very fast and purpose-built - Lets you define: - directory structure - filename templates - sequence handling 👉 This is honestly the *spiritual successor* to what you're using Darktable for. --- # 🧪 Option 3 — roll your own (if you want full control) If you want to go full William-mode and own the pipeline: ### Stack - `exiftool` → extract metadata - `bash` or `python` → grouping logic - `jhead` or `exiv2` → optional tweaks ### Concept 1. Group files by timestamp (within ~1 second) 2. Assign a shared ID 3. Rename both RAW + JPG using same base This gives you **perfect control**, but honestly: > You’d just be rebuilding what `exiftool` already does well. --- # 🧠 Real Talk (what you should actually do) - If you want **pure CLI + reliability** → go `exiftool` - If you want **visual ingest + less thinking** → go Rapid Photo Downloader - If you want **maximum control + future automation hooks (Obsidian, etc.)** → wrap `exiftool` in a script --- # ⚡ Bonus: make it one command forever Drop this into a script: ```bash #!/usr/bin/env bash SRC="$1" exiftool -r \ -d "/media/wm/Aperture/RAW/%Y/%m/%Y-%m-%d/WP3_%Y%m%d-%H%M%S_%%-04c.%%e" \ "-FileName<CreateDate" \ "$SRC" ``` Then run: ```bash ./ingest.sh /media/wm/card ``` Done. No Darktable. --- # TL;DR - Darktable is overkill for what you're using it for - `exiftool` replaces your workflow almost perfectly - It **does handle RAW+JPG pairs correctly** - Wrap it in a script and you’ve got a cleaner, faster pipeline --- If you want, I can take this one step further and wire it into your **syskit-style backup + ingest flow** so your photos land exactly where they belong *and* get logged into your system automatically. --- > [!NOTE] > > > Drop this into a script > > Can you explain this to me a bit? --- Absolutely—this is worth understanding properly because once it clicks, you’ll never go back to GUI ingest again. --- # 🧠 What “drop this into a script” actually means You’re turning that long command into a **reusable command of your own**—like a personal tool. Instead of pasting a giant one-liner every time, you create something like: ```bash ingest.sh ``` …and run it like: ```bash ./ingest.sh /path/to/card ``` --- # 🔧 Step-by-step (clean and practical) ## 1. Create the script file ```bash micro ~/ingest.sh ``` Paste this in: ```bash #!/usr/bin/env bash SRC="$1" exiftool -r \ -d "/media/wm/Aperture/RAW/%Y/%m/%Y-%m-%d/WP3_%Y%m%d-%H%M%S_%%-04c.%%e" \ "-FileName<CreateDate" \ "$SRC" ``` --- ## 2. Make it executable ```bash chmod +x ~/ingest.sh ``` This is what allows `./ingest.sh` to run like a real command. --- ## 3. Run it ```bash ~/ingest.sh /media/wm/card ``` Or if you're already in home: ```bash ./ingest.sh /media/wm/card ``` --- # 🧩 What each line is doing (no fluff) ## `#!/usr/bin/env bash` - This tells Linux: > “Run this file using bash” - It’s what makes the file a *script* instead of just text --- ## `SRC="$1"` - `$1` = the **first argument** you pass in - So when you run: ```bash ./ingest.sh /media/wm/card ``` `$1` becomes: ```bash /media/wm/card ``` 👉 This makes your script flexible—you can point it anywhere --- ## The `exiftool` command This is the engine. ### `-r` - Process everything **recursively** (folders inside folders) --- ### `-d "..."` This is your **entire naming + folder blueprint** ```bash /media/wm/Aperture/RAW/%Y/%m/%Y-%m-%d/ ``` Creates: ``` 2026/ 04/ 2026-04-13/ ``` --- ### File naming part ```bash WP3_%Y%m%d-%H%M%S_%%-04c.%%e ``` Break it down: |Part|Meaning| |---|---| |`WP3_`|your prefix| |`%Y%m%d`|date| |`-%H%M%S`|time| |`%%-04c`|sequence number (0001, 0002…)| |`%%e`|file extension| --- ### Why `%%` instead of `%` Because this is inside a quoted string, `exiftool` needs `%%` to mean “literal %”. --- ## `"-FileName<CreateDate"` This is the **most important line** It tells exiftool: > “Rename files based on the EXIF CreateDate” ### Why this matters This is what enables: - Correct timestamps - RAW + JPG pairing - Consistent grouping --- ## `"$SRC"` This is where your files are coming from Example: ```bash /media/wm/card ``` --- # ⚡ What happens when you run it Let’s say your card has: ``` IMG_1234.CR2 IMG_1234.JPG ``` After running: ```bash ./ingest.sh /media/wm/card ``` You get: ``` /media/wm/Aperture/RAW/2026/04/2026-04-13/ WP3_20260413-135358_0001.CR2 WP3_20260413-135358_0001.JPG ``` 👉 Same base name. That’s the Darktable behavior you wanted. --- # 🔥 Optional upgrade (you’ll want this) Add this line above `exiftool`: ```bash set -e ``` Now the script will **stop immediately if something breaks** instead of silently continuing. --- # 🧠 Why this is better than aliases You mentioned aliases getting messy—this is exactly why scripts exist. |Alias|Script| |---|---| |fragile quoting|clean structure| |hard to read|readable| |hard to expand|easy to add logic| |limited|full programming power| --- # 💡 Where to put it permanently Since you already use: ``` ~/.local/bin ``` Do this: ```bash mv ~/ingest.sh ~/.local/bin/ingest chmod +x ~/.local/bin/ingest ``` Now you can run it anywhere: ```bash ingest /media/wm/card ``` --- # TL;DR - You’re turning a command into your own reusable tool - `$1` lets you pass in the memory card path - `exiftool` handles naming + pairing automatically - Putting it in `~/.local/bin` makes it globally available --- If you want, the next step is dialing this into your **full pipeline**: - auto-detect mounted SD card - ingest - verify - optional backup to Birch That’s where this gets *really* powerful. --- *Confirmed script* --- Yes—this looks solid. The only thing worth tightening up is exactly what you asked about: --- # ⚠️ `set -e` — what it actually does ```bash set -e ``` This tells Bash: > **“If *any* command fails, stop the script immediately.”** --- ## 🧠 Why that matters here Without it: - `exiftool` could fail on some files - the script keeps going - you end up with **partial imports and no obvious warning** With it: - first error → script stops instantly - you know something went wrong right away 👉 This is exactly what you want for file operations. --- ## ⚠️ The catch (important) `set -e` is **blunt**. It will exit on *any* non-zero return code—even harmless ones. In your case: - `exiftool` is generally clean - but it **can return non-zero** for minor issues (like a file missing metadata) That means your script *could stop early* even when most files are fine. --- # 🔧 Practical upgrade (recommended) If you want **strict but not fragile**, do this instead: ```bash #!/usr/bin/env bash set -euo pipefail SRC="$1" exiftool -r \ -d "/media/wm/Aperture/RAW/%Y/%m/%Y-%m-%d/WP3_%Y%m%d-%H%M%S_%%-04c.%%e" \ "-FileName<CreateDate" \ "$SRC" ``` --- ## 🔍 What you just added ### `-u` (undefined variables) Stops if you accidentally reference something like `$SRCC` ### `-o pipefail` If you later pipe commands, failures won’t get hidden --- # 🧠 Real-world behavior with your script ### Good case - Files import cleanly → script completes ### Minor EXIF issue - Script might stop early (because of `set -e`) - You rerun or inspect → safe, no silent corruption --- # 💡 If you want it *bulletproof* You could soften it like this: ```bash exiftool ... "$SRC" || { echo "Ingest failed. Check output." exit 1 } ``` Now you control the failure message instead of Bash silently exiting. --- # 🧠 Bottom line - Your script is already **correct** - `set -e` is **good discipline** for this kind of workflow - Adding `-u -o pipefail` makes it more resilient - The only tradeoff: it may stop on minor EXIF quirks (which is usually a good thing) --- If you want the next level: we can add **dry-run mode**, **logging**, or even a **“don’t overwrite existing files” safeguard**—that’s where this starts feeling like a real ingest system instead of just a command. --- > [!NOTE] > Now what do i do in order to run it from anywhere? --- You’re one step away. The goal is simple: > Make your script behave like a **native command** (`ls`, `cp`, etc.) --- # ✅ Step 1 — Move it into your PATH You already have this in your PATH: ``` /home/wm/.local/bin ``` So put the script there: ```bash mv ~/ingest.sh ~/.local/bin/ingest ``` *(You can rename it here—dropping `.sh` is normal for commands)* --- # ✅ Step 2 — Make sure it’s executable ```bash chmod +x ~/.local/bin/ingest ``` --- # ✅ Step 3 — Reload your shell Either: ```bash source ~/.bashrc ``` or just open a new terminal. --- # ✅ Step 4 — Run it from anywhere Now this works globally: ```bash ingest /media/wm/card ``` No `./`, no full path. Just like a real command. --- # 🔍 Quick sanity check Run: ```bash which ingest ``` You should see: ``` /home/wm/.local/bin/ingest ``` If you do, you’re good. --- # ⚠️ If it *doesn’t* work Check your PATH: ```bash echo $PATH ``` Make sure this is in there: ``` /home/wm/.local/bin ``` If not, add it to your `~/.bashrc`: ```bash export PATH="$HOME/.local/bin:$PATH" ``` Then: ```bash source ~/.bashrc ``` --- # 💡 Optional polish (worth doing) ### 1. Add a usage guard Right now, if you forget the argument, it’ll behave weird. Add this near the top: ```bash if [ -z "$1" ]; then echo "Usage: ingest /path/to/source" exit 1 fi ``` --- ### 2. Give it a real “tool” feel Rename it cleanly: ```bash mv ~/.local/bin/ingest ~/.local/bin/wp3-ingest ``` Then: ```bash wp3-ingest /media/wm/card ``` --- # 🧠 What you just built You now have: - A **custom CLI command** - That runs anywhere - That replaces a chunk of Darktable - That fits your existing Linux workflow perfectly --- # TL;DR ```bash mv ~/ingest.sh ~/.local/bin/ingest chmod +x ~/.local/bin/ingest ``` Then: ```bash ingest /media/wm/card ``` --- If you want to take it one notch further, the next move is: - auto-detect the mounted SD card - run ingest automatically - optionally eject when done That’s where it starts feeling *dangerously efficient*. --- > [!NOTE] > Okay just to confirm this command wont MOVE the files off an SD card right, they will just copy? --- Good instinct to double-check—this is where people accidentally shoot themselves in the foot. ## ❗ Short answer **Yes — your current command *moves* files, not copies them.** --- ## 🧠 Why that’s happening This part of your script is the culprit: ```bash "-FileName<CreateDate" ``` In ExifTool, when you **write to `FileName`**, you are: > **Renaming (and relocating) the original file** So with your `-d` path pointing to: ```bash /media/wm/Aperture/RAW/... ``` 👉 Files are physically **moved off the SD card into that directory** --- ## 🔥 What that means in practice From your SD card: ```plaintext IMG_1234.CR2 IMG_1234.JPG ``` ➡️ becomes (on your drive): ```plaintext /media/wm/Aperture/RAW/2026/04/2026-04-13/ WP3_20260413-135358_0001.CR2 WP3_20260413-135358_0001.JPG ``` …and the originals are **gone from the SD card** --- # ✅ If you want COPY instead (recommended) Use `-o` (output directory) instead of renaming in place. ### Updated command ```bash exiftool -r \ -d "/media/wm/Aperture/RAW/%Y/%m/%Y-%m-%d/WP3_%Y%m%d-%H%M%S_%%-04c.%%e" \ "-FileName<CreateDate" \ -o "/media/wm/Aperture/RAW/%Y/%m/%Y-%m-%d/" \ "$SRC" ``` --- ## ⚠️ But here’s the catch `-o` **copies**, but: - It does NOT behave exactly like renaming - Filename templating gets a little awkward - You can end up with unexpected naming unless handled carefully --- # 💡 The cleaner approach (what I recommend) Split it into **two steps**: --- ## 1️⃣ Copy everything first ```bash rsync -av "$SRC/" /media/wm/Aperture/_incoming/ ``` - Fast - Safe - Leaves SD card untouched --- ## 2️⃣ Then run your rename ```bash ingest /media/wm/Aperture/_incoming ``` --- ## 🧠 Why this is better - No risk of losing files if something breaks - You keep a clean staging area (`_incoming`) - You can verify before renaming - Matches how pro ingest workflows are usually done --- # ⚖️ Honest comparison |Method|Safe|Clean|Recommended| |---|---|---|---| |Direct `exiftool` (your current)|❌|✅|risky| |`exiftool -o`|⚠️|⚠️|meh| |`rsync → ingest`|✅|✅|✔️ best| --- # 🔒 My blunt recommendation Don’t move directly off the SD card. Do this instead: ```bash rsync -av /media/wm/card/ /media/wm/Aperture/_incoming/ ingest /media/wm/Aperture/_incoming ``` Then format the card when you’re done. --- # TL;DR - Your current script **moves files off the card** - If you want safety → **copy first, then rename** - Best workflow = `rsync` → `ingest` --- If you want, I can wire this into a single command so you can just run: ```bash ingest-card ``` …and it: - detects the SD card - copies files - renames them - optionally ejects the card That’s where it becomes a *real* ingestion system. --- > [!NOTE] > Part of that strange workflow I mentioned earlier, that is what I actually did I would copy them from my SD card first then do the renaming once they were copied over. I don't mind doing that again. It's just a precaution in case something goes sidways. > > Is it possible to make a script to perform the rsync command you mentioned? --- Yes—this is exactly the right instinct. You’re basically describing a **safe ingest pipeline**, and yeah, you can absolutely script the `rsync` step cleanly. Let’s build it properly so it feels like a real tool, not just a throwaway command. --- # 🔧 Simple `rsync` script (clean + safe) Create this: ```bash micro ~/.local/bin/copy-card ``` Paste: ```bash #!/usr/bin/env bash set -euo pipefail SRC="$1" DEST="/media/wm/Aperture/_incoming" if [ -z "$SRC" ]; then echo "Usage: copy-card /path/to/source" exit 1 fi echo "📥 Copying files from: $SRC" echo "📁 Destination: $DEST" rsync -av --progress "$SRC"/ "$DEST"/ echo "✅ Copy complete." ``` --- # 🔑 What this gives you ### `rsync -av --progress` - `-a` → preserves timestamps, structure (important for photos) - `-v` → shows what’s happening - `--progress` → you see file-by-file progress (nice for large cards) --- # 🚀 Make it executable ```bash chmod +x ~/.local/bin/copy-card ``` Now you can run: ```bash copy-card /media/wm/card ``` --- # 🧠 Why this is the right move You now have: 1. **Step 1:** Safe copy ```bash copy-card /media/wm/card ``` 2. **Step 2:** Rename + organize ```bash ingest /media/wm/Aperture/_incoming ``` 👉 This mirrors how pros do it: - ingest to staging - verify - then process --- # 🔥 Optional upgrades (worth it) ## 1. Don’t copy duplicates Add this flag: ```bash --ignore-existing ``` So: ```bash rsync -av --progress --ignore-existing "$SRC"/ "$DEST"/ ``` 👉 Prevents re-copying if you run it twice --- ## 2. Show a clean summary at the end Add: ```bash --stats ``` Now you get a nice recap: - total files - transfer speed - size --- ## 3. Dry run mode (VERY useful) Add a toggle: ```bash DRY_RUN=false if [ "${2:-}" = "--dry" ]; then DRY_RUN=true fi RSYNC_FLAGS="-av --progress --stats" if [ "$DRY_RUN" = true ]; then RSYNC_FLAGS="$RSYNC_FLAGS --dry-run" echo "⚠️ Dry run mode enabled" fi rsync $RSYNC_FLAGS "$SRC"/ "$DEST"/ ``` Run like: ```bash copy-card /media/wm/card --dry ``` 👉 Shows what *would* happen without copying --- # 💡 If you want it to feel *slick* Rename it: ```bash mv ~/.local/bin/copy-card ~/.local/bin/wp3-copy ``` Now your workflow becomes: ```bash wp3-copy /media/wm/card wp3-ingest /media/wm/Aperture/_incoming ``` That’s clean. Consistent. Yours. --- # 🧠 Bigger picture (you’re building something solid) What you’ve now got is: - deterministic file structure - EXIF-driven naming - RAW+JPG pairing preserved - safe ingest staging That’s not “wonky”—that’s **intentional workflow design**. --- # TL;DR Yes—script it like this: ```bash rsync -av --progress "$SRC"/ /media/wm/Aperture/_incoming/ ``` Wrap it in `~/.local/bin`, make it executable, and you now have: ```bash copy-card /media/wm/card ``` --- If you want next step: we can combine **copy + ingest + optional eject** into a single command so you just plug the card in and run one thing. --- > [!NOTE] > what should the rsync script look like with the toggle added? --- ``` copy-card /media/wm/card --dry ``` Use this version: ```bash #!/usr/bin/env bash set -euo pipefail SRC="${1:-}" DEST="/media/wm/Aperture/_incoming" if [ -z "$SRC" ]; then echo "Usage: copy-card /path/to/source [--dry]" exit 1 fi RSYNC_FLAGS="-av --progress --stats --ignore-existing" if [ "${2:-}" = "--dry" ]; then RSYNC_FLAGS="$RSYNC_FLAGS --dry-run" echo "Dry run mode enabled. No files will be copied." fi echo "Copying from: $SRC" echo "Copying to: $DEST" mkdir -p "$DEST" rsync $RSYNC_FLAGS "$SRC"/ "$DEST"/ echo "Copy complete." ``` Then save it as: ```bash ~/.local/bin/copy-card ``` Make it executable: ```bash chmod +x ~/.local/bin/copy-card ``` Run normally: ```bash copy-card /media/wm/card ``` Run as a dry test: ```bash copy-card /media/wm/card --dry ``` The `--dry` version shows what would copy, but **does not actually copy anything**. --- > [!NOTE] > How would this look? Also is there a way to set the auto-numbering sequence to start at a certain number? --- You’re right at the point where this turns into a **real ingest tool**. Let’s tighten both parts: --- # 🧩 1. Combined “copy + ingest (+ optional eject)” script This rolls everything into one command. ```bash #!/usr/bin/env bash set -euo pipefail SRC="${1:-}" DEST="/media/wm/Aperture/_incoming" FINAL="/media/wm/Aperture/RAW" if [ -z "$SRC" ]; then echo "Usage: wp3-ingest /path/to/card [--dry]" exit 1 fi DRY=false if [ "${2:-}" = "--dry" ]; then DRY=true echo "⚠️ Dry run enabled" fi RSYNC_FLAGS="-av --progress --stats --ignore-existing" if [ "$DRY" = true ]; then RSYNC_FLAGS="$RSYNC_FLAGS --dry-run" fi echo "📥 Step 1: Copying from $SRC → $DEST" mkdir -p "$DEST" rsync $RSYNC_FLAGS "$SRC"/ "$DEST"/ echo "📸 Step 2: Renaming + organizing" if [ "$DRY" = false ]; then exiftool -r \ -d "$FINAL/%Y/%m/%Y-%m-%d/WP3_%Y%m%d-%H%M%S_%%-04c.%%e" \ "-FileName<CreateDate" \ "$DEST" else echo "Skipping exiftool due to dry run" fi echo "🧹 Step 3: Done" # Optional eject if [ "$DRY" = false ]; then echo "🔌 Attempting to eject source..." udisksctl unmount -b "$(df "$SRC" | tail -1 | awk '{print $1}')" 2>/dev/null || true fi echo "✅ Workflow complete." ``` --- ## 🧠 What this gives you One command: ```bash wp3-ingest /media/wm/card ``` Does: 1. Copy → `_incoming` 2. Rename → final structure 3. Attempt eject Optional: ```bash wp3-ingest /media/wm/card --dry ``` --- # 🔢 2. About the sequence number (`%%-04c`) This is the part you asked about—and here’s the blunt truth: ## ❗ You **cannot directly set a starting number** in exiftool `%-04c` (or `%%-04c` in your case) means: > “Auto-increment starting from 1 for each group” --- ## 🧠 How it actually works For files with the same timestamp: ```plaintext WP3_20260413-135358_0001.CR2 WP3_20260413-135358_0002.CR2 ``` It resets per timestamp group. --- # ⚠️ If you *really* want a custom starting number You have three realistic approaches: --- ## ✅ Option A — Offset after the fact (simple + reliable) Run a second pass: ```bash exiftool '-FileName<${FileName;s/_(\d{4})/_'.sprintf("%04d",$1+390).'/e}' -r /your/folder ``` 👉 Adds +390 to every sequence number --- ## ✅ Option B — Pre-seed numbering via existing files If the destination already contains files like: ```plaintext WP3_20260413-135358_0390.CR2 ``` Then new files will continue at: ```plaintext 0391, 0392... ``` 👉 This is actually the **cleanest method** --- ## 🧠 Real-world recommendation Don’t fight exiftool here. Use: ```bash %%-04c ``` …and let: - timestamps define grouping - sequence define uniqueness --- # 💡 If you want global numbering (different behavior) That’s a different problem entirely. You’d need: - a counter file - or a database-style increment - or a custom script layer I can build that if you want—but it’s a different system than EXIF-based grouping. --- # 🔥 My straight answer - Your combined script idea → **excellent** - Your instinct to keep `_incoming` → **correct** - Custom sequence start → **not native, workaround needed** - Best practice → let sequence auto-start at `0001` per timestamp --- # TL;DR ### Your new workflow ```bash wp3-ingest /media/wm/card ``` ### Sequence numbers - Auto start at `0001` - Cannot directly change start - Can offset after or seed with existing files --- If you want next level: we can make it: - auto-detect the SD card (no path needed) - skip `_incoming` if empty - log every ingest (fits your Obsidian/system mindset perfectly) That’s when this stops being a script and becomes *your system*.