Skip to content

Repository files navigation

darktable-scripts

Lua modules for darktable.

move_by_capture_year

Moves the selected images into per-year folders under a destination root, taking the year from each image's EXIF capture date.

An image captured on 2025:06:14 is moved to <destination root>/2025/.

This is the counterpart to move_by_tag_hierarchy: that one turns tags into folders, this one flattens folders down to plain year buckets. Tag first, move second — see tag_by_folder_hierarchy — and the folder structure survives as tags.

Requirements

darktable with Lua API 7.0.0 or newer. Developed and tested against darktable 5.6 on macOS. No external software.

Installation

Copy the script into darktable's config directory and require it:

cp move_by_capture_year.lua ~/.config/darktable/lua/
echo 'require "move_by_capture_year"' >> ~/.config/darktable/luarc

Or install it with script_manager. Either way, restart darktable — the module uses destroy_method = "hide", so script_manager does not re-execute the file and a reload from the module list will not pick up a changed script.

move by capture year then appears in the lighttable right panel. It may be collapsed; click the header to open it.

Usage

  1. Select the images to move.
  2. destination root — the directory the year folders are created in, e.g. /Volumes/Photos. The button below the field opens a directory chooser.
  3. dry run — report what would happen without moving anything. On by default.

The status line reports moved N, N already in place, N skipped, N failed. The per-image detail always goes to the darktable log; start darktable with -d lua to see it on the console.

The run is cancellable from the progress bar. Each image is committed individually, so stopping partway leaves the already-moved images done and the rest untouched.

The capture date, and nothing else

The year comes from the image's EXIF capture date — the same value darktable's own image time module edits. Nothing else is consulted: not the file's modification time, not the folder it currently sits in, not the filename.

An image whose capture date is missing, unreadable, or outside the range a photograph could have been taken in is skipped rather than guessed at. A wrong year folder is silently wrong; a skip is not. Fix the date in image time and run the module again.

What is skipped

  • no capture date at all
  • a capture date that cannot be read, or a year before 1826 or beyond next year — darktable reports a missing date as an all-zero timestamp, and a camera whose clock has been reset reports 1970
  • another file, or an orphaned XMP sidecar, already at the target path
  • the image's file is missing on disk
  • duplicates sharing one file on disk whose capture dates fall in different years — the file is left alone rather than moved under one duplicate's date

Skips and failures are counted separately: a skip means the image was left alone deliberately, a failure means something went wrong (mkdir failed, no film roll could be created, or darktable raised while moving). Both are logged with a reason.

Notes

  • An image already sitting in its year folder is counted as already in place and left alone, so a re-run over a part-migrated selection is safe.
  • Tags are never touched.
  • darktable moves the XMP sidecar along with the image.
  • On macOS and Windows, paths that differ only in case name the same directory. The module accounts for this: a destination differing only in case from an existing film roll reuses that film roll instead of creating a second one for the same directory.
  • Settings persist between sessions in darktablerc under lua/move_by_capture_year/.

A word of caution

This module moves files on disk. Run it with dry run on first and read the log before committing to a real run, and make sure you have a backup of both your photos and ~/.config/darktable/library.db.

move_by_tag_hierarchy

Moves every image carrying a hierarchical tag into a folder tree that mirrors the tag tree, then optionally detaches the tags that the path now expresses.

The move by tag hierarchy module in darktable's lighttable right panel, showing the root tag and destination root fields, the scope selector, the option check buttons and the move images button

The module in the lighttable right panel. The settings shown are one configuration, not the defaults — see Usage.

An image tagged Places|UK|London is moved to:

include root tag in path destination
on <destination root>/Places/UK/London/
off <destination root>/UK/London/

An image tagged only with the root tag lands in the destination root itself (or in <destination root>/Places).

Requirements

darktable with Lua API 7.0.0 or newer. Developed and tested against darktable 5.6 on macOS. No external software.

Installation

Copy the script into darktable's config directory and require it:

cp move_by_tag_hierarchy.lua ~/.config/darktable/lua/
echo 'require "move_by_tag_hierarchy"' >> ~/.config/darktable/luarc

Or install it with script_manager. Either way, restart darktable — the module uses destroy_method = "hide", so script_manager does not re-execute the file and a reload from the module list will not pick up a changed script.

move by tag hierarchy then appears in the lighttable right panel. It may be collapsed; click the header to open it.

Usage

  1. root tag — the tag to process, e.g. Places. Every image tagged with it or with anything below it (Places|UK|London) is a candidate.
  2. destination root — the top of the folder tree, e.g. /Volumes/Photos/Sorted. The button below the field opens a directory chooser.
  3. images — process every tagged image in the library, or only the selected ones.
  4. include root tag in path — see the table above.
  5. remove hierarchy tags — after a verified move, detach the root tag and every tag below it that the image carries.
  6. remove matching flat tags — additionally detach non-hierarchical tags whose name exactly matches a path component, e.g. a plain London tag on an image moved to .../UK/London. Off by default: a flat tag can share a name with a place by accident (nice, bath), and detaching a tag cannot be undone.
  7. dry run — report what would happen without moving anything or changing tags. On by default.

The status line reports moved N, N already in place, N skipped, N failed, N tags removed. The per-image detail always goes to the darktable log; start darktable with -d lua to see it on the console.

The run is cancellable from the progress bar. Each image is committed individually, so stopping partway leaves the already-moved images done and the rest untouched.

Which tag decides the destination

The deepest matching tag wins. An image tagged both Places|UK and Places|UK|London goes to .../UK/London, because the first is an ancestor of the second. An image tagged Places|UK and Places|France is skipped — those disagree about where it should go.

Tag components are sanitized for use as directory names: <>:"/\|?*, control characters, and trailing dots and spaces are replaced or trimmed.

What is skipped

  • tags pointing at two different destinations
  • another file, or an orphaned XMP sidecar, already at the target path
  • the image's file is missing on disk
  • duplicates sharing one file on disk that want different destinations — the file is left alone rather than moved under one duplicate's tags

Skips and failures are counted separately: a skip means the image was left alone deliberately, a failure means something went wrong (mkdir failed, no film roll could be created, or darktable raised while moving). Both are logged with a reason.

Notes

  • Tags are detached only after the move has been verified, so a failed move never leaves an image stripped of the tags that describe where it belongs.
  • darktable moves the XMP sidecar along with the image.
  • On macOS and Windows, paths that differ only in case name the same directory. The module accounts for this: a destination differing only in case from an existing film roll reuses that film roll instead of creating a second one for the same directory.
  • darktable's own internal tags are never processed or detached.
  • Settings persist between sessions in darktablerc under lua/move_by_tag_hierarchy/.

A word of caution

This module moves files on disk. Run it with dry run on first and read the log before committing to a real run, and make sure you have a backup of both your photos and ~/.config/darktable/library.db.

prune_ancestor_tags

Detaches the hierarchical tags an image no longer needs because a deeper tag on the same image already says the same thing. An image tagged Places|USA|Virginia|Roanoke does not also need Places, Places|USA or Places|USA|Virginia.

This is the companion to prune_flat_tags, and the two ask different questions. prune_flat_tags looks at the tag list across the whole library and matches a flat name against any level of any hierarchy. prune_ancestor_tags looks at one image at a time and compares whole paths from the root, so it only ever removes a tag that is literally an ancestor of another tag on that same image.

Requirements

darktable with Lua API 7.0.0 or newer. Developed and tested against darktable 5.6 on macOS. No external software.

Installation

cp prune_ancestor_tags.lua ~/.config/darktable/lua/
echo 'require "prune_ancestor_tags"' >> ~/.config/darktable/luarc

Or install it with script_manager, then restart darktable. prune ancestor tags appears in the lighttable right panel.

Usage

  1. Select the images in lighttable.
  2. dry run — on by default. Reports what would be detached without changing anything. The per-tag report always goes to the darktable log; start darktable with -d lua to see it on the console.
  3. Press prune ancestor tags.

What counts as redundant

A tag is redundant on an image when another tag on that same image begins with it and continues with a |. Whole levels only, and only from the root:

on the image verdict
Places|USA beside Places|USA|Virginia redundant
Places beside Places|USA|Virginia redundant
Styles|Film beside Film Details|Film|Fujifilm kept — a different root
Places|US beside Places|USA|Virginia kept — US is not a whole level
Virginia beside Places|USA|Virginia|Roanoke kept — not a path from the root
places|usa beside Places|USA|Virginia kept — tags are case sensitive

A branch that is nobody's ancestor is kept, so an image carrying several leaves under one tree keeps every one of them and loses only the levels above:

Subjects|Outdoors                          detached
Subjects|Outdoors|Nature                   detached
Subjects|Outdoors|Nature|Landscape         detached
Subjects|Outdoors|Nature|Landscape|Details kept
Subjects|Outdoors|Nature|Landscape|Rocks   kept
Subjects|Outdoors|Nature|Plants            kept

Notes

  • No tag is ever deleted from the library. This module only detaches tags from images. A tag left attached to nothing stays in the tag list until darktable's own delete unused tags is run, and a tag other images still carry is untouched.
  • darktable's internal tags (darktable|…) are never detached, and are never used to justify detaching anything else.
  • Every tag is looked up again by its full name before it is detached, and the image's tag list is read back afterwards. A tag whose name does not resolve to itself is refused rather than detached, and a detach that reports success while changing nothing is counted as a failure, not a detachment. Each detach is written to the log as it happens rather than after it is verified, so a change that cannot be undone always leaves a record even if the run then fails.
  • Sidecar files. darktable writes XMP sidecars either on import or after every edit, depending on write_sidecar_files in your darktablerc. On the "after edit" setting a real run means one sidecar write per image touched, so prune in batches. Whether removing an ancestor tag also shrinks the flat keyword list an XMP exports is a question about darktable's writer, not its Lua API — check a sidecar before pruning a set you export to other software.
  • Grouped images. The plan is built per image, from that image's own tags, so select whole groups — expand them, or turn grouping off — before a run. Two members carrying identical tags stay identical only if both are in the selection; prune one without the other and they diverge. Note also that pruning cannot make differing tags match: stripping a redundant ancestor from a leader whose member never had the deeper tag pushes the two further apart, not closer.
  • The run can be cancelled from the progress bar. Tags already detached stay detached.
  • Running it twice is harmless: the second run finds nothing left to do.

A word of caution

Detaching a tag cannot be undone from Lua. Run it with dry run on first and read the log, and back up ~/.config/darktable/library.db before a real run.

prune_flat_tags

Removes flat (non-hierarchical) tags whose name a hierarchical tag already carries. A plain London tag says nothing that Places|UK|London does not.

Where move_by_tag_hierarchy's remove matching flat tags option cleans up the images it moves, this module works on the tag list itself, across the whole library, without touching a single file.

Requirements

darktable with Lua API 7.0.0 or newer. Developed and tested against darktable 5.6 on macOS. No external software.

Installation

cp prune_flat_tags.lua ~/.config/darktable/lua/
echo 'require "prune_flat_tags"' >> ~/.config/darktable/luarc

Or install it with script_manager, then restart darktable. prune flat tags appears in the lighttable right panel.

Usage

  1. match — what counts as a match. With any level of a hierarchy the flat tag UK matches Places|UK|London; with leaf level only, only London does.

  2. ignore case — off by default, because darktable's tags are case sensitive: london and London are two different tags.

  3. action — what happens to a matching flat tag:

    action effect
    detach where the hierarchy tag is present detaches the flat tag only from images that already carry a hierarchical tag containing the name; deletes the tag once no image is left carrying it
    delete the tag from the library deletes the flat tag outright, detaching it from every image, including images with no hierarchical tag saying the same thing
  4. dry run — report what would happen without changing any tag. On by default.

The status line reports deleted N tags, kept N, N detachments, N refused, N skipped, N failed. Refused tags were rejected while planning, skipped ones while running. The detail always goes to the darktable log — including a line naming every single image that loses a tag, since a detachment cannot be undone. Start darktable with -d lua to see it on the console.

The reported detachment count is read back from the library (the drop in the tag's image count), not from the number of calls made. A call that reports success while changing nothing therefore shows up as 0 detachments and a failure, which is the behaviour that made the first version of this module misreport a run entirely.

The run is cancellable from the progress bar, during the scan as well as during the pruning. Each tag is committed individually, so stopping partway leaves the tags already handled done and the rest untouched.

Which is the right action

detach where the hierarchy tag is present is the safe one and the default. Consider two images: one tagged Places|UK|London and London, one tagged only London. The first is genuinely redundant; the second is the only record that the image has anything to do with London. Detaching keeps that record and reports the tag as kept, so it shows up in the log rather than disappearing silently.

Choose delete the tag from the library when the flat tags are known leftovers — an import from software that had no tag hierarchy, say — and the hierarchy is now the only version you want to keep.

Every tag is verified by name before it is touched

Walking dt.tags is not sufficient to identify a flat tag. The walk has been observed mid-session to yield entries named for a single level of a hierarchy — Grass for Subjects|Outdoors|Nature|Landscape|Grass — which report that hierarchy's images and accept detach and delete calls without changing anything in the database. At darktable startup the same walk returns full paths, so the behaviour is not stable across a session.

The module therefore treats the walk as a source of names only, and requires two independent things to agree before touching anything:

  1. the name must round-trip through dt.tags.find() and come back as a tag with the identical name, and
  2. at least one of that tag's images must report carrying a tag of that exact name when asked directly, via dt.tags.get_tags().

The second condition is what makes the first meaningful. find() returning an object is only find()'s word; an image reports a hierarchy as Subjects|Outdoors|Nature|Landscape|Grass, never as a bare Grass, so an entry named for a hierarchy level cannot be corroborated and is refused. Both modes check this — delete mode has no other evidence at all about the tag it is about to remove.

Anything refused is logged as REFUSED with its reason and counted in the status line. Tags are resolved again at execution time rather than trusting objects captured while planning, and a delete only follows a detachment that has been verified against the library.

A consequence worth knowing: a flat tag attached to no images cannot be corroborated either, so it is refused rather than deleted. darktable's own delete_unused_tags script is the right tool for those.

Notes

  • In delete the tag from the library mode the tag is removed in one operation, which detaches it from every image by itself. It is not detached image by image first: that would only add ways for a run to stop half done.
  • ignore case is byte-wise, so it does not equate café with CAFÉ, and it does not see decomposed and precomposed accents (NFD vs NFC) as the same name — realistic on macOS. Both are false negatives: such tags are left alone rather than wrongly pruned.
  • darktable's own internal tags (darktable|...) are ignored on both sides: they are never pruned, and they never make a flat tag a match — a flat jpeg tag is not matched by darktable|format|jpeg.
  • Everything is resolved before anything is changed, because detaching or deleting a tag mutates the tag list being walked.
  • A tag that fails partway (an image that vanished from the library, say) is counted as failed and left in place rather than half-pruned; the run continues with the next tag.
  • Settings persist between sessions in darktablerc under lua/prune_flat_tags/.

A word of caution

Detaching and deleting tags cannot be undone from Lua. Run it with dry run on first and read the log, and back up ~/.config/darktable/library.db.

RL_out_sharp

Richardson-Lucy output sharpening with G'MIC, applied to the exported file after darktable has resized it.

darktable's own sharpening runs in the pixelpipe, before the export is scaled down, so it cannot correct the softness the downscale itself introduces. This runs G'MIC's deblur_richardsonlucy on the finished file instead.

This is a reworked version of darktable's bundled contrib/RL_out_sharp.lua by Marco Carrarini. The original only offered its own export target, with a single output folder and no file name template, and failed silently when that folder was not set.

Requirements

darktable with Lua API 7.0.0 or newer. Developed and tested against darktable 5.6 on macOS. Needs the G'MIC command line tool (brew install gmic), and exiftool to carry the metadata and ICC profile over to the sharpened file.

Installation

darktable bundles the original under the same name, so turn off RL output sharpening in script_manager first, so the two do not both register the same export target, module and export handler. Then:

cp RL_out_sharp.lua ~/.config/darktable/lua/
echo 'require "RL_out_sharp"' >> ~/.config/darktable/luarc

Restart darktable, then set the G'MIC executable under preferences > lua options (e.g. /opt/homebrew/bin/gmic).

RL output sharpening appears in the lighttable right panel, directly above export.

Usage

Sharpening file on disk exports. Export as usual with file on disk: the file darktable writes — at the path its template expands to, in its format and quality — is sharpened in place. JPEG and TIFF (8, 16 and 32-bit float) are supported; anything else is left alone, with a message saying so.

The module's settings for list holds a default (other exports) entry and one entry per export preset, each with its own sharpen file on disk exports switch, sigma and iterations. A preset uses the default settings until something is changed for it; hovering the list shows the selected preset's path template and whether it is still on the defaults.

The RL output sharpen export target. The original export target is still there: it exports to a temporary JPEG or TIFF, sharpens it, and writes a JPEG to one output folder. It uses the default sigma and iterations.

sigma 0.7, 10 iterations is a sensible start for web-size exports. Raise iterations for a crisper result; lower sigma if edges start to halo.

How an export is matched to a preset

The export event does not say which preset is in use, so the module matches on the file on disk path template, which it reads from each preset in data.db. That makes batch exports across several presets pick up each preset's settings. Two consequences:

  • Presets need distinct templates. Two presets sharing one template share settings — the alphabetically later one wins.
  • A preset edited in the export module without being saved no longer matches, and gets the default settings. The on-screen message names the entry used, e.g. sharpening a.jpg (Substack posts) ....

New and changed presets are picked up on entering lighttable, and at export time when a template matches nothing.

Notes

  • G'MIC splits its output argument on commas, so it only ever sees plain temporary file names; spaces and commas in the real path are fine. The result is copied into place, so it works across volumes.
  • When G'MIC fails, the message gives its exit code and points to RL_out_sharp.log in darktable's temporary directory, which holds G'MIC's own error.
  • Without exiftool the sharpened file carries no metadata and no ICC profile.
  • Settings persist in darktablerc under lua/RL_out_sharp/, the same keys as the original script.

License

Unlike the rest of this repository, RL_out_sharp.lua is derived from a GPL-licensed darktable script and stays under the GNU General Public License, version 3 or later.

select_grouped

Adds select grouped and select ungrouped to the select module in the lighttable right panel.

darktable's collect and filter modules only offer a fixed set of criteria, and group membership is not one of them — it cannot be added from Lua. This is the next best thing: select grouped selects the images of the current collection that share a group with at least one other image, and select ungrouped selects the rest. From there the selected image[s] module's group / ungroup buttons act on what you found.

Requirements

darktable with Lua API 7.0.0 or newer. Developed and tested against darktable 5.6 on macOS. No external software.

Installation

cp select_grouped.lua ~/.config/darktable/lua/
echo 'require "select_grouped"' >> ~/.config/darktable/luarc

Or install it with script_manager, then restart darktable.

Notes

  • Grouping is a property of the library, not of the collection. An image whose only group partner sits in another film roll still counts as grouped.
  • darktable puts every image in a group, so an image on its own is a group of one. "Grouped" therefore means the group has a second member, not that the image has a group at all — which is why the two buttons together do not simply select everything twice.
  • Both buttons ask the database for each image's group, so a large collection takes a moment. The run is cancellable from the progress bar; cancelling applies the partial selection and says so in the message.
  • Each button is also available as a shortcut, under lua/select grouped and lua/select ungrouped in the shortcuts dialog. The shortcuts act on the whole current collection.

set_time_from_filename

Sets the capture date of the selected images from a date encoded in their filename, for scans and exports that carry no usable EXIF date.

darktable's own image time module is the better tool when one date applies to the whole selection. This one exists for the case it does not: a folder of scans where every file needs a different date, and every date is already in the filename.

Requirements

darktable with Lua API 7.0.0 or newer. Developed and tested against darktable 5.6 on macOS. No external software.

Installation

Copy the script into darktable's config directory and require it:

cp set_time_from_filename.lua ~/.config/darktable/lua/
echo 'require "set_time_from_filename"' >> ~/.config/darktable/luarc

Or install it with script_manager. Either way, restart darktable — the module uses destroy_method = "hide", so script_manager does not re-execute the file and a reload from the module list will not pick up a changed script.

set capture time from filename then appears in the lighttable right panel. It may be collapsed; click the header to open it.

Usage

  1. Select the images.
  2. only images with no capture date — leave images that already have a date alone. On by default: overwriting a date darktable read from the file itself is the destructive case, and it cannot be undone from here.
  3. dry run — report what would be set without changing anything. On by default.

The status line reports set N, N already correct, N skipped, N failed. The per-image detail always goes to the darktable log, including the previous value of every date it replaces; start darktable with -d lua to see it on the console.

How the filename is read

The first run of eight or more digits, whose first eight are YYYYMMDD:

filename capture date
P20260104-02.jpg 2026:01:04 00:00:00
i2026022401.jpg 2026:02:24 00:00:00

In the second case the run is ten digits long — the trailing 01 is a sequence number, not a time.

A six-digit HHMMSS immediately after the date is read as the time, whether glued on or separated by a single non-digit:

filename capture date
20240415_115416_IMG_4198.jpg 2024:04:15 11:54:16

What is skipped

  • no run of eight or more digits in the filename
  • the digits are not a real calendar date — the month, the day of the month and leap years are all checked, so 20260229 is refused and 20240229 is not
  • the year is before 1826 or beyond next year, which is what keeps a hash or a serial number from becoming a date. 00000808.rw2 gives year 0000, and the eight-digit run inside a SHA-512 filename gave year 8958; both are rejected
  • the image already has a capture date, unless the guard above is turned off

Notes

  • Every write is read back afterwards. A write that reports success while changing nothing is counted as a failure, not as a date set.
  • An image already holding exactly the filename date is counted as already correct rather than rewritten, so re-running is idempotent.
  • This changes the database, and the XMP sidecar if darktable is writing them. It does not rewrite the EXIF inside the image file.
  • Settings persist between sessions in darktablerc under lua/set_time_from_filename/.

A word of caution

Turning off only images with no capture date overwrites capture dates that darktable read from the files themselves, and that cannot be undone from Lua. Run with dry run on first — the log names the old value of every date it would replace — and make sure you have a backup of ~/.config/darktable/library.db.

tag_by_folder_hierarchy

Tags the selected images with a hierarchical tag that mirrors the folder they sit in. It is the inverse of move_by_tag_hierarchy: that module turns tags into folders, this one turns folders into tags. It never moves, renames or deletes anything.

Only the deepest folder is tagged. With a folder root of /Volumes/Photos:

image tag
/Volumes/Photos/Events/img1.raw Events
/Volumes/Photos/Events/Holiday/img2.raw Events|Holiday
/Volumes/Photos/Events/Holiday/Christmas/img3.raw Events|Holiday|Christmas

Each image gets exactly one tag, for its own folder — never one tag per level.

Requirements

darktable with Lua API 7.0.0 or newer. Developed and tested against darktable 5.6 on macOS. No external software.

Installation

cp tag_by_folder_hierarchy.lua ~/.config/darktable/lua/
echo 'require "tag_by_folder_hierarchy"' >> ~/.config/darktable/luarc

Or install it with script_manager, then restart darktable. tag by folder hierarchy appears in the lighttable right panel.

Usage

  1. folder root — the folder the tag tree starts below, e.g. /Volumes/Photos. The root itself never appears in a tag. The button under the field opens a directory chooser.
  2. tag prefix — optional. Folders turns Events|Holiday into Folders|Events|Holiday, keeping the whole tree under one top-level tag.
  3. dry run — report what would be tagged without attaching anything. On by default.

Select the images and press tag images. The status line reports tagged N with N tags, N already tagged, N categories, N skipped, N failed. The per-image detail always goes to the darktable log; start darktable with -d lua to see it on the console.

The run is cancellable from the progress bar. Each image is committed individually, so stopping partway leaves the already-tagged images done and the rest untouched.

Folders with no images become categories

A folder that holds only subfolders becomes a category rather than a tag, and tagging only the deepest folder is what gets you there. darktable stores one row per tag name: attaching Events|Holiday does not create Events. Events shows up in the tag dictionary as a parent node with no images and nothing to attach — the dictionary even offers set as a tag for it — which is exactly how a category behaves.

The run counts and logs those levels so you can see which folders ended up as categories:

tag_by_folder_hierarchy: category (no images of its own): 'Events|Holiday'

The one thing the module cannot do is anything about a level that already exists as a tag. darktable's Lua API exposes a tag's name and synonyms and nothing else — the category flag lives in a column Lua can neither read nor write. Such a level is reported and left alone:

tag_by_folder_hierarchy: 'Travel' holds no images but already exists in the
library; lua cannot read the category flag, so check it in the tag dictionary

That level may already be a category — Lua cannot tell — so the log says what is known rather than guessing. Check it in the tagging module's dictionary.

Whether a folder holds images is answered from the library's film rolls, not from the selection — a folder whose images simply were not selected is not a category.

What is skipped

  • images outside the folder root
  • images directly in the folder root, when no tag prefix is set: there is no folder to name them after

Skips and failures are counted separately: a skip means the image was left alone deliberately, a failure means something went wrong. Both are logged with a reason, including when nothing could be tagged at all. A run that gives up before it starts — no selection, no folder root — logs GAVE UP with the reason, so a run never leaves a header in the log with no conclusion under it.

Notes

  • The module acts on darktable's usual act on set: the selection, or the image under the mouse pointer when one is hovered and nothing is selected.
  • The module only ever attaches. It never detaches, so an image tagged for its old folder that has since moved keeps both tags; the stale one has to go by hand. prune_flat_tags will not help there — both tags are hierarchical.
  • Every attachment is verified by asking the image what it carries afterwards. An attach that reports success while changing nothing is counted as a failure, not as a tag.
  • An image that already carries its folder tag is counted as already tagged and left alone, so re-running the module is cheap and idempotent.
  • | separates tag levels, so a folder name containing one would silently add a level. Those characters, and control characters, become _.
  • Runs of whitespace in a folder name collapse to a single space, so Myrtle Beach May 2022 tags as Myrtle Beach May 2022. A doubled space is invisible in the tag dictionary and would otherwise make two tags out of what reads as one name. Leading and trailing whitespace is trimmed off each level.
  • On macOS and Windows, paths that differ only in case name the same folder. The module accounts for this: a tag differing from an existing one only in case is not created, and the library's existing spelling is reused. On Linux the two folders are genuinely different and get two tags.
  • The folder root is not required to exist. Images on an unmounted volume still have a path to read a tag off; a missing root is noted in the log, not refused.
  • darktable's own internal tags are refused as a prefix and never produced.
  • Settings persist between sessions in darktablerc under lua/tag_by_folder_hierarchy/.

License

GNU Lesser General Public License, version 2.1 or later. See LICENSE.

The exception is RL_out_sharp.lua, which is derived from a darktable script and remains under the GNU General Public License, version 3 or later, as its header states.

About

Lua modules for darktable. Includes move_by_tag_hierarchy: move tagged images into a folder tree matching the tag hierarchy.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages