From b70fdc0ecf64192be36eb8508d9fcef3b34150c1 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Fri, 7 Feb 2025 14:29:45 -0500 Subject: [PATCH 01/14] official/apply_camera_style - Fixed decoding errors caused by #18332. --- official/apply_camera_style.lua | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/official/apply_camera_style.lua b/official/apply_camera_style.lua index aac49d16..b162ffef 100644 --- a/official/apply_camera_style.lua +++ b/official/apply_camera_style.lua @@ -52,7 +52,9 @@ local log = require "lib/dtutils.log" local MODULE = "apply_camera_style" local DEFAULT_LOG_LEVEL = log.info local TMP_DIR = dt.configuration.tmp_dir -local STYLE_PREFIX = "_l10n_darktable camera styles|" +local STYLE_PREFIX = "_l10n_darktable|_l10n_camera styles|" +local MAKER = 3 +local STYLE = 4 -- path separator local PS = dt.configuration.running_os == "windows" and "\\" or "/" @@ -222,25 +224,25 @@ local function get_camera_styles() log.msg(log.debug, "got " .. style.name) local parts = du.split(style.name, "|") - parts[2] = string.lower(parts[2]) - log.msg(log.debug, "maker is " .. parts[2]) + parts[MAKER] = string.lower(parts[MAKER]) + log.msg(log.debug, "maker is " .. parts[MAKER]) - if not acs.styles[parts[2]] then - acs.styles[parts[2]] = {} - acs.styles[parts[2]]["styles"] = {} - acs.styles[parts[2]]["patterns"] = {} + if not acs.styles[parts[MAKER]] then + acs.styles[parts[MAKER]] = {} + acs.styles[parts[MAKER]]["styles"] = {} + acs.styles[parts[MAKER]]["patterns"] = {} end - if parts[3] then - if not string.match(parts[3], "]") then - table.insert(acs.styles[parts[2]].styles, style) + if parts[STYLE] then + if not string.match(parts[STYLE], "]") then + table.insert(acs.styles[parts[MAKER]].styles, style) local processed_pattern = process_pattern(parts[#parts]) - table.insert(acs.styles[parts[2]].patterns, processed_pattern) + table.insert(acs.styles[parts[MAKER]].patterns, processed_pattern) log.msg(log.debug, "pattern for " .. style.name .. " is " .. processed_pattern) else - local processed_patterns = process_set(parts[3]) + local processed_patterns = process_set(parts[STYLE]) for _, pat in ipairs(processed_patterns) do - table.insert(acs.styles[parts[2]].styles, style) - table.insert(acs.styles[parts[2]].patterns, pat) + table.insert(acs.styles[parts[MAKER]].styles, style) + table.insert(acs.styles[parts[MAKER]].patterns, pat) log.msg(log.debug, "pattern for " .. style.name .. " is " .. pat) end end From 3001a0dc1a0cf1c229aac5a802a819f2ba6943aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Dura-Kov=C3=A1cs?= Date: Sun, 4 May 2025 23:23:33 +0200 Subject: [PATCH 02/14] Add manual coordinate input option to geoToolbox --- contrib/geoToolbox.lua | 64 ++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/contrib/geoToolbox.lua b/contrib/geoToolbox.lua index 01a14eea..4f17b46b 100644 --- a/contrib/geoToolbox.lua +++ b/contrib/geoToolbox.lua @@ -1,6 +1,7 @@ --[[ This file is part of darktable, copyright (c) 2016 Tobias Jakobs + copyright (c) 2025 Balázs Dura-Kovács darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -63,21 +64,30 @@ gT.event_registered = false local labelDistance = dt.new_widget("label") labelDistance.label = _("distance:") -local label_copy_gps_lat = dt.new_widget("check_button") +local checkbox_copy_gps_lat = dt.new_widget("check_button") { - label = _("latitude: "), value = true } -local label_copy_gps_lon = dt.new_widget("check_button") +local entry_gps_lat = dt.new_widget("entry") +{ + placeholder = _("latitude") +} +local checkbox_copy_gps_lon = dt.new_widget("check_button") { - label = _("longitude: "), value = true } -local label_copy_gps_ele = dt.new_widget("check_button") +local entry_gps_lon = dt.new_widget("entry") +{ + placeholder = _("longitude") +} +local checkbox_copy_gps_ele = dt.new_widget("check_button") { - label = _("elevation: "), value = true } +local entry_gps_ele = dt.new_widget("entry") +{ + placeholder = _("elevation") +} -- local function select_with_gps() @@ -294,20 +304,20 @@ local function copy_gps() copy_gps_have_data = false else copy_gps_have_data = true - if (image.latitude and label_copy_gps_lat.value) then + if (image.latitude and checkbox_copy_gps_lat.value) then copy_gps_latitude = image.latitude end - if (image.longitude and label_copy_gps_lon.value) then + if (image.longitude and checkbox_copy_gps_lon.value) then copy_gps_longitude = image.longitude end - if (image.elevation and label_copy_gps_ele.value) then + if (image.elevation and checkbox_copy_gps_ele.value) then copy_gps_elevation = image.elevation end end - label_copy_gps_lat.label = _("latitude: ") .. copy_gps_latitude - label_copy_gps_lon.label = _("longitude: ") .. copy_gps_longitude - label_copy_gps_ele.label = _("elevation: ") .. copy_gps_elevation + entry_gps_lat.text = copy_gps_latitude + entry_gps_lon.text = copy_gps_longitude + entry_gps_ele.text = copy_gps_elevation return end @@ -317,14 +327,14 @@ local function paste_gps(image) local sel_images = dt.gui.action_images for jj,image in ipairs(sel_images) do - if (label_copy_gps_lat.value) then - image.latitude = copy_gps_latitude + if (checkbox_copy_gps_lat.value) then + image.latitude = entry_gps_lat.text end - if (label_copy_gps_lon.value) then - image.longitude = copy_gps_longitude + if (checkbox_copy_gps_lon.value) then + image.longitude = entry_gps_lon.text end - if (label_copy_gps_ele.value) then - image.elevation = copy_gps_elevation + if (checkbox_copy_gps_ele.value) then + image.elevation = entry_gps_ele.text end end end @@ -663,9 +673,21 @@ gT.widget = dt.new_widget("box") tooltip = _("copy GPS data"), clicked_callback = copy_gps }, - label_copy_gps_lat, - label_copy_gps_lon, - label_copy_gps_ele, + dt.new_widget("box"){ + orientation = "horizontal", + checkbox_copy_gps_lat, + entry_gps_lat + }, + dt.new_widget("box"){ + orientation = "horizontal", + checkbox_copy_gps_lon, + entry_gps_lon + }, + dt.new_widget("box"){ + orientation = "horizontal", + checkbox_copy_gps_ele, + entry_gps_ele + }, dt.new_widget("button") { label = _("paste GPS data"), From 25afb419247450a6541ea9f6c3218f054d4dbfb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Dura-Kov=C3=A1cs?= Date: Tue, 6 May 2025 19:37:09 +0200 Subject: [PATCH 03/14] Create tooltips for geoToolbox --- contrib/geoToolbox.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/geoToolbox.lua b/contrib/geoToolbox.lua index 4f17b46b..9f9c1e04 100644 --- a/contrib/geoToolbox.lua +++ b/contrib/geoToolbox.lua @@ -70,6 +70,7 @@ local checkbox_copy_gps_lat = dt.new_widget("check_button") } local entry_gps_lat = dt.new_widget("entry") { + tooltip = _("latitude (editable)"), placeholder = _("latitude") } local checkbox_copy_gps_lon = dt.new_widget("check_button") @@ -78,6 +79,7 @@ local checkbox_copy_gps_lon = dt.new_widget("check_button") } local entry_gps_lon = dt.new_widget("entry") { + tooltip = _("longitude (editable)"), placeholder = _("longitude") } local checkbox_copy_gps_ele = dt.new_widget("check_button") @@ -86,6 +88,7 @@ local checkbox_copy_gps_ele = dt.new_widget("check_button") } local entry_gps_ele = dt.new_widget("entry") { + tooltip = _("elevation (editable)"), placeholder = _("elevation") } -- @@ -690,8 +693,8 @@ gT.widget = dt.new_widget("box") }, dt.new_widget("button") { - label = _("paste GPS data"), - tooltip = _("paste GPS data"), + label = _("apply GPS data to image"), + tooltip = _("apply GPS data to image"), clicked_callback = paste_gps }, separator2,-------------------------------------------------------- From f8d49ccd017654801b77667963b0fa282c4c9bc1 Mon Sep 17 00:00:00 2001 From: Michael Reiger Date: Sat, 24 May 2025 19:31:23 +0200 Subject: [PATCH 04/14] Modified apply_camera_style script so that the application of a camera style does not mark the image as changed --- official/apply_camera_style.lua | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/official/apply_camera_style.lua b/official/apply_camera_style.lua index b748ef95..2675b672 100644 --- a/official/apply_camera_style.lua +++ b/official/apply_camera_style.lua @@ -60,6 +60,9 @@ local PS = dt.configuration.running_os == "windows" and "\\" or "/" -- command separator local CS = dt.configuration.running_os == "windows" and "&" or ";" +-- tag name for changed images +local changed_tag_name = "darktable|changed" + -- - - - - - - - - - - - - - - - - - - - - - - - -- A P I C H E C K -- - - - - - - - - - - - - - - - - - - - - - - - @@ -313,7 +316,7 @@ local function normalize_maker(maker) return maker end -local function has_style_tag(image, tag_name) +local function has_tag(image, tag_name) local log_level = set_log_level(acs.log_level) @@ -387,11 +390,19 @@ local function apply_style_to_images(images) for i, pattern in ipairs(acs.styles[maker].patterns) do if string.match(model, pattern) or (i == #acs.styles[maker].patterns and string.match(pattern, "generic")) then - local tag_name = "darktable|style|" .. acs.styles[maker].styles[i].name - if not has_style_tag(image, tag_name) then + local style_tag_name = "darktable|style|" .. acs.styles[maker].styles[i].name + if not has_tag(image, style_tag_name) then + local already_changed = has_tag(image, changed_tag_name) image:apply_style(acs.styles[maker].styles[i]) no_match = false log.msg(log.info, "applied style " .. acs.styles[maker].styles[i].name .. " to " .. image.filename) + if not already_changed then + local ct = dt.tags.find(changed_tag_name) + if ct then + log.msg(log.debug, "detaching tag " .. changed_tag_name .. " from image " .. image.filename) + image:detach_tag(ct) + end + end end log.log_level(loglevel) break From 8cd10dce68531895b6a9a21a9f156a8bafe9f2a6 Mon Sep 17 00:00:00 2001 From: Michael Reiger Date: Sun, 1 Jun 2025 12:50:59 +0200 Subject: [PATCH 05/14] Revert "Modified apply_camera_style script so that the application of a camera style does not mark the image as changed" because that attempt did not work. This reverts commit f8d49ccd017654801b77667963b0fa282c4c9bc1. --- official/apply_camera_style.lua | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/official/apply_camera_style.lua b/official/apply_camera_style.lua index 2675b672..b748ef95 100644 --- a/official/apply_camera_style.lua +++ b/official/apply_camera_style.lua @@ -60,9 +60,6 @@ local PS = dt.configuration.running_os == "windows" and "\\" or "/" -- command separator local CS = dt.configuration.running_os == "windows" and "&" or ";" --- tag name for changed images -local changed_tag_name = "darktable|changed" - -- - - - - - - - - - - - - - - - - - - - - - - - -- A P I C H E C K -- - - - - - - - - - - - - - - - - - - - - - - - @@ -316,7 +313,7 @@ local function normalize_maker(maker) return maker end -local function has_tag(image, tag_name) +local function has_style_tag(image, tag_name) local log_level = set_log_level(acs.log_level) @@ -390,19 +387,11 @@ local function apply_style_to_images(images) for i, pattern in ipairs(acs.styles[maker].patterns) do if string.match(model, pattern) or (i == #acs.styles[maker].patterns and string.match(pattern, "generic")) then - local style_tag_name = "darktable|style|" .. acs.styles[maker].styles[i].name - if not has_tag(image, style_tag_name) then - local already_changed = has_tag(image, changed_tag_name) + local tag_name = "darktable|style|" .. acs.styles[maker].styles[i].name + if not has_style_tag(image, tag_name) then image:apply_style(acs.styles[maker].styles[i]) no_match = false log.msg(log.info, "applied style " .. acs.styles[maker].styles[i].name .. " to " .. image.filename) - if not already_changed then - local ct = dt.tags.find(changed_tag_name) - if ct then - log.msg(log.debug, "detaching tag " .. changed_tag_name .. " from image " .. image.filename) - image:detach_tag(ct) - end - end end log.log_level(loglevel) break From e054bfc59d0c8af5734574dc11d6f01d09c839a2 Mon Sep 17 00:00:00 2001 From: Michael Reiger Date: Sun, 1 Jun 2025 13:12:56 +0200 Subject: [PATCH 06/14] Added script to automatically tag manually modified images --- README.md | 1 + contrib/auto_tag_modified.lua | 129 ++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 contrib/auto_tag_modified.lua diff --git a/README.md b/README.md index 8caa5c2e..043e0b50 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Name|Standalone|OS |Purpose ----|:--------:|:---:|------- [AutoGrouper](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/autogrouper)|Yes|LMW|Group images together by time [autostyle](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/autostyle)|Yes|LMW|Automatically apply styles on import +[auto_tag_modified](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/auto_tag_modified)|Yes|LMW|Automatically tag images which have been manually modified in the darkroom change_group_leader|Yes|LMW|Change which image leads group [clear_GPS](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/clear_gps)|Yes|LMW|Reset GPS information for selected images [CollectHelper](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/collecthelper)|Yes|LMW|Add buttons to selected images module to manipulate the collection diff --git a/contrib/auto_tag_modified.lua b/contrib/auto_tag_modified.lua new file mode 100644 index 00000000..ea7d94d6 --- /dev/null +++ b/contrib/auto_tag_modified.lua @@ -0,0 +1,129 @@ +--[[ + + auto_tag_modified.lua - automatically take a snapshot when an image is loaded in darkroom + + Copyright (C) 2025 Michael Reiger , modeled after auto_snapshot.lua by Bill Ferguson . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +]] +--[[ + auto_tag_modified - + + automatically tag an image when it has been manually modified in the darkroom, that is if its history has changed at the time it is closed from the darkroom. + (This will not catch applying styles from the light table though.) + + ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT + None + + USAGE + * start the script from script_manager + * open an image in darkroom, and close it after making an operation that changes the history + + BUGS, COMMENTS, SUGGESTIONS + Michael Reiger + + CHANGES +]] + +local dt = require "darktable" +local du = require "lib/dtutils" +local log = require "lib/dtutils.log" + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- C O N S T A N T S +-- - - - - - - - - - - - - - - - - - - - - - - - + +local MODULE = "auto_tag_modified" +local DEFAULT_TAG_NAME = "darktable manually modified" +local DEFAULT_LOG_LEVEL = log.error + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- A P I C H E C K +-- - - - - - - - - - - - - - - - - - - - - - - - + +du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need + + +-- - - - - - - - - - - - - - - - - - - - - - - - - - +-- I 1 8 N +-- - - - - - - - - - - - - - - - - - - - - - - - - - + +local gettext = dt.gettext.gettext + +local function _(msgid) + return gettext(msgid) +end + + +-- - - - - - - - - - - - - - - - - - - - - - - - - - +-- S C R I P T M A N A G E R I N T E G R A T I O N +-- - - - - - - - - - - - - - - - - - - - - - - - - - + +local script_data = {} + +script_data.destroy = nil -- function to destory the script +script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet +script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again +script_data.show = nil -- only required for libs since the destroy_method only hides them + +script_data.metadata = { + name = _("auto tag modified"), -- name of script + purpose = _("automatically tag an image when it has been manually modified in darkroom"), -- purpose of script + author = "Michael Reiger ", -- your name and optionally e-mail address + help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/auto_tag_modified/" -- URL to help/documentation +} + + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- L O G L E V E L +-- - - - - - - - - - - - - - - - - - - - - - - - + +log.log_level(DEFAULT_LOG_LEVEL) + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- N A M E S P A C E +-- - - - - - - - - - - - - - - - - - - - - - - - + +local auto_tag_modified = {} + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- P R E F E R E N C E S +-- - - - - - - - - - - - - - - - - - - - - - - - + +dt.preferences.register(MODULE, "auto_modified_tag_name", "string", "auto_tag_modified - " .. _("tag name to use for auto tagging modified images"), + _("tag name to use for auto tagging modified images"), DEFAULT_TAG_NAME) + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- D A R K T A B L E I N T E G R A T I O N +-- - - - - - - - - - - - - - - - - - - - - - - - + +local function destroy() + dt.destroy_event(MODULE, "darkroom-image-history-changed") +end + +script_data.destroy = destroy + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- E V E N T S +-- - - - - - - - - - - - - - - - - - - - - - - - + +dt.register_event(MODULE, "darkroom-image-history-changed", + function(event, image) + local tag_name = dt.preferences.read(MODULE, "auto_modified_tag_name", "string") + local tag = dt.tags.create(tag_name) + dt.tags.attach(tag, image) + end +) + +return script_data From 84fea20ee5c5ac0a86031f66ca406f2b4f69847b Mon Sep 17 00:00:00 2001 From: Michael Reiger Date: Sun, 1 Jun 2025 13:28:44 +0200 Subject: [PATCH 07/14] fixed script desription --- contrib/auto_tag_modified.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/auto_tag_modified.lua b/contrib/auto_tag_modified.lua index ea7d94d6..bceda423 100644 --- a/contrib/auto_tag_modified.lua +++ b/contrib/auto_tag_modified.lua @@ -1,6 +1,6 @@ --[[ - auto_tag_modified.lua - automatically take a snapshot when an image is loaded in darkroom + auto_tag_modified.lua - automatically tag an image when it has been manually modified in darkroom Copyright (C) 2025 Michael Reiger , modeled after auto_snapshot.lua by Bill Ferguson . From b9cc0dccce648887ae2b54ee5ad0f00fcd1c9f91 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Sun, 1 Jun 2025 16:36:52 -0400 Subject: [PATCH 08/14] official/apply_camera_style - made spaces in camera name conditional matches as some are squashed and others (Olympus) are not. --- official/apply_camera_style.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/official/apply_camera_style.lua b/official/apply_camera_style.lua index b162ffef..c82a91b3 100644 --- a/official/apply_camera_style.lua +++ b/official/apply_camera_style.lua @@ -166,6 +166,8 @@ local function process_pattern(pattern) end -- escape dashes pattern = string.gsub(pattern, "%-", "%%-") + -- make spaces optional + pattern = string.gsub(pattern, " ", " ?") -- until we end up with a set, I'll defer set processing, i.e. [...] -- anchor the pattern to ensure we don't short match pattern = "^" .. pattern .. "$" From ea79d10b1108b78fa55c432083c26f6bdd442d81 Mon Sep 17 00:00:00 2001 From: deekayhd <53017684+deekayhd@users.noreply.github.com> Date: Tue, 1 Jul 2025 13:55:47 +0200 Subject: [PATCH 09/14] Update string.lua initialize datetime_taken with proper value if no exif_datetime_taken can be found (e.g. in TIFF files) --- lib/dtutils/string.lua | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/dtutils/string.lua b/lib/dtutils/string.lua index 48221f19..2db97298 100644 --- a/lib/dtutils/string.lua +++ b/lib/dtutils/string.lua @@ -734,16 +734,33 @@ function dtutils_string.build_substitute_list(image, sequence, variable_string, local labels = get_colorlabels(image) - local eyear, emon, eday, ehour, emin, esec, emsec + local datetime_taken = "" + local use_millisecs = false if dt.preferences.read("darktable", "lighttable/ui/milliseconds", "bool") and is_api_9_1 then + use_millisecs = true + end + + if image.exif_datetime_taken and image.exif_datetime_taken ~= "" then + datetime_taken = image.exif_datetime_taken + else + if use_millisecs then + datetime_taken = "0000:00:00 00:00:00.0" + else + datetime_taken = "0000:00:00 00:00:00" + end + end + + local eyear, emon, eday, ehour, emin, esec, emsec + if use_millisecs then eyear, emon, eday, ehour, emin, esec, emsec = - string.match(image.exif_datetime_taken, "(%d+):(%d+):(%d+) (%d+):(%d+):(%d+)%.(%d+)$") + string.match(datetime_taken, "(%d+):(%d+):(%d+) (%d+):(%d+):(%d+)%.(%d+)$") else emsec = "0" eyear, emon, eday, ehour, emin, esec = - string.match(image.exif_datetime_taken, "(%d+):(%d+):(%d+) (%d+):(%d+):(%d+)$") + string.match(datetime_taken, "(%d+):(%d+):(%d+) (%d+):(%d+):(%d+)$") end + local version_multi = #image:get_group_members() > 1 and image.duplicate_index or "" local replacements = {dtutils_string.get_basename(image.film.path),-- ROLL.NAME @@ -781,8 +798,8 @@ function dtutils_string.build_substitute_list(image, sequence, variable_string, eyear, -- EXIF.YEAR string.sub(eyear, 3), -- EXIF.YEAR.SHORT emon, -- EXIF.MONTH - os.date("%B", exiftime2systime(image.exif_datetime_taken)), -- EXIF.MONTH.LONG - os.date("%b", exiftime2systime(image.exif_datetime_taken)), -- EXIF.MONTH.SHORT + os.date("%B", exiftime2systime(datetime_taken)), -- EXIF.MONTH.LONG + os.date("%b", exiftime2systime(datetime_taken)), -- EXIF.MONTH.SHORT eday, -- EXIF.DAY ehour, -- EXIF.HOUR "", -- EXIF.HOUR.AMPM From d7aeb433400ee13ad8094351bd0a24ac2c097548 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Sat, 5 Jul 2025 12:39:36 -0400 Subject: [PATCH 10/14] contrib/RL_out_sharp - changed MODULE to MODULE_NAME so that message would print and script wouldn't crash --- contrib/RL_out_sharp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/RL_out_sharp.lua b/contrib/RL_out_sharp.lua index b65581c9..b4208a3d 100644 --- a/contrib/RL_out_sharp.lua +++ b/contrib/RL_out_sharp.lua @@ -106,7 +106,7 @@ local function preserve_metadata(original, sharpened) if exiftool then dtsys.external_command("exiftool -overwrite_original_in_place -tagsFromFile " .. original .. " " .. sharpened) else - dt.print_log(MODULE .. " exiftool not found, metadata not preserved") + dt.print_log(MODULE_NAME .. " exiftool not found, metadata not preserved") end end From 68291414f33b97b105af0ddb0bd45feeba43d385 Mon Sep 17 00:00:00 2001 From: sjjh <2787214+sjjh@users.noreply.github.com> Date: Sun, 13 Jul 2025 16:56:22 +0200 Subject: [PATCH 11/14] Update README.md: added link to script_manager docs just for convenience: added a link to dt documenation for script manager module in the 'enable' section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8caa5c2e..139988b1 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ If you don't have %LOCALAPPDATA%\darktable you have to start dartable at least o When darktable starts it looks for a file name `~/.config/darktable/luarc` (`%LOCALAPPDATA%\darktable\luarc` for windows) and reads it to see which scripts to include. The file is a plain text file with entries of the form `require "/"` where directory is the directory containing the scripts, from the above list, and name is the name from the above list. To include GIMP the line would be `require "contrib/gimp"`. -The recommended way to enable and disable specific scripts is using the script manager module. To use script manager do the following: +The recommended way to enable and disable specific scripts is using the [script manager](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/tools/script_manager/) module. To use script manager do the following: ### Linux or MacOS From 8af5476f8d588d839bf1ce33cee17b9db6f29b78 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Sun, 2 Nov 2025 16:59:32 -0500 Subject: [PATCH 12/14] lib/dtutils/string - fixed string substitution sequence to accept start argument of more than 1 digit --- lib/dtutils/string.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtutils/string.lua b/lib/dtutils/string.lua index 2db97298..f3afc345 100644 --- a/lib/dtutils/string.lua +++ b/lib/dtutils/string.lua @@ -1011,7 +1011,7 @@ local function treat(var_string) log.msg(log.info, "ret_val is " .. ret_val) elseif string.match(var_string, "SEQUENCE%[") then - local width, start = string.match(var_string, "(%d+),(%d)") + local width, start = string.match(var_string, "(%d+),(%d+)") local seq_val = tonumber(substitutes[var]) local pat = "%0" .. width .. "d" substitutes[var_string] = string.format(pat, start + (seq_val - 1)) From 193a615772d82700fb6537fe7d48afa029d78684 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Sat, 8 Nov 2025 21:27:14 -0500 Subject: [PATCH 13/14] official/regenerate_thumbnails - add a script to fix skull thumbnails by dropping the cache for the image and regenerating it. Adds a button to the actions on selected images module. Also has a shortcut to apply the script by hovering and triggering the shortcut. --- official/regenerate_thumbnails.lua | 264 +++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 official/regenerate_thumbnails.lua diff --git a/official/regenerate_thumbnails.lua b/official/regenerate_thumbnails.lua new file mode 100644 index 00000000..cbef00c6 --- /dev/null +++ b/official/regenerate_thumbnails.lua @@ -0,0 +1,264 @@ +--[[ + + regenerate_thumbnails.lua - regenerate mipmap cache for selected images + + Copyright (C) 2025 Bill Ferguson . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +]] +--[[ + regenerate_thumbnails - regenerate mipmap cache for selected images + + regenerate_thumbnails drops the cached thumbnail for each selected image + and generates a new thumbnail. + + ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT + None + + USAGE + * enable the script in script_manager + * assign a shortcut, if desired, to apply the script by hovering + over a skull and using the shortcut to regenerate the thumbnail + + BUGS, COMMENTS, SUGGESTIONS + Bill Ferguson + + CHANGES +]] + +local dt = require "darktable" +local du = require "lib/dtutils" +-- local df = require "lib/dtutils.file" +-- local ds = require "lib/dtutils.string" +-- local dtsys = require "lib/dtutils.system" +local log = require "lib/dtutils.log" +-- local debug = require "darktable.debug" + + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- A P I C H E C K +-- - - - - - - - - - - - - - - - - - - - - - - - + +du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need + + +-- - - - - - - - - - - - - - - - - - - - - - - - - - +-- I 1 8 N +-- - - - - - - - - - - - - - - - - - - - - - - - - - + +local gettext = dt.gettext.gettext + +local function _(msgid) + return gettext(msgid) +end + + +-- - - - - - - - - - - - - - - - - - - - - - - - - - +-- S C R I P T M A N A G E R I N T E G R A T I O N +-- - - - - - - - - - - - - - - - - - - - - - - - - - + +local script_data = {} + +script_data.destroy = nil -- function to destory the script +script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet +script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again +script_data.show = nil -- only required for libs since the destroy_method only hides them + +script_data.metadata = { + name = _("regenerate_thumbnails"), -- visible name of script + purpose = _("regenerate mipmap cache for selected images"), -- purpose of script + author = "Bill Ferguson ", -- your name and optionally e-mail address + help = "https://docs.darktable.org/lua/development/lua.scripts.manual/scripts/official/regenerate_thumbnails" -- URL to help/documentation +} + + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- C O N S T A N T S +-- - - - - - - - - - - - - - - - - - - - - - - - + +local MODULE = "regenerate_thumbnails" +local DEFAULT_LOG_LEVEL = log.info +local TMP_DIR = dt.configuration.tmp_dir + +-- path separator +local PS = dt.configuration.running_os == "windows" and "\\" or "/" + +-- command separator +local CS = dt.configuration.running_os == "windows" and "&" or ";" + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- L O G L E V E L +-- - - - - - - - - - - - - - - - - - - - - - - - + +log.log_level(DEFAULT_LOG_LEVEL) + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- N A M E S P A C E +-- - - - - - - - - - - - - - - - - - - - - - - - + +local regenerate_thumbnails = {} + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- G L O B A L V A R I A B L E S +-- - - - - - - - - - - - - - - - - - - - - - - - + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- P R E F E R E N C E S +-- - - - - - - - - - - - - - - - - - - - - - - - + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- A L I A S E S +-- - - - - - - - - - - - - - - - - - - - - - - - + +local namespace = regenerate_thumbnails +local rt = regenerate_thumbnails + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- F U N C T I O N S +-- - - - - - - - - - - - - - - - - - - - - - - - + +------------------- +-- helper functions +------------------- + +local function set_log_level(level) + local old_log_level = log.log_level() + log.log_level(level) + return old_log_level +end + +local function restore_log_level(level) + log.log_level(level) +end + +local function pref_read(name, pref_type) + local old_log_level = set_log_level(sm.log_level) + + log.msg(log.debug, "name is " .. name .. " and type is " .. pref_type) + + local val = dt.preferences.read(MODULE, name, pref_type) + + log.msg(log.debug, "read value " .. tostring(val)) + + restore_log_level(old_log_level) + return val +end + +local function pref_write(name, pref_type, value) + local old_log_level = set_log_level(sm.log_level) + + log.msg(log.debug, "writing value " .. tostring(value) .. " for name " .. name) + + dt.preferences.write(MODULE, name, pref_type, value) + + restore_log_level(old_log_level) +end + +local function stop_job() + rt.job.valid = false +end + +local function generate_thumbnails(images) + local has_job = false + + if #images > 50 then + rt.job = dt.gui.create_job("regenerating thumbnails", true, stop_job) + has_job = true + end + + for count, image in ipairs(images) do + image:drop_cache() + image:generate_cache(true, 1, 3) + if has_job then + if count % 10 == 0 then + rt.job.percent = count / #images + end + end + end + if has_job then + rt.job.valid = false + end +end + +local function update_button_sensitivity() + if #dt.gui.action_images > 0 then + dt.gui.libs.image.set_sensitive(MODULE, true) + else + dt.gui.libs.image.set_sensitive(MODULE, false) + end +end + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- M A I N P R O G R A M +-- - - - - - - - - - - - - - - - - - - - - - - - + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- U S E R I N T E R F A C E +-- - - - - - - - - - - - - - - - - - - - - - - - + +dt.gui.libs.image.register_action( + MODULE, + _("generate thumbnails"), + function(event, images) + generate_thumbnails(images) + end, + _("generate thumbnails") +) + +update_button_sensitivity() + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- D A R K T A B L E I N T E G R A T I O N +-- - - - - - - - - - - - - - - - - - - - - - - - + +local function destroy() + dt.destroy_event(MODULE, "selection-changed") + dt.destroy_event(MODULE, "mouse-over-image-changed") + dt.gui.libs.image.destroy_action(MODULE, "regenerate thumbnails") +end + +script_data.destroy = destroy + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- E V E N T S +-- - - - - - - - - - - - - - - - - - - - - - - - + +dt.register_event(MODULE, "shortcut", + function(event, shortcut) + local images = dt.gui.action_images + if images then + generate_thumbnails(images) + end + end, "regenerate thumbnails" +) + +dt.register_event(MODULE, "selection-changed", + function(event) + update_button_sensitivity() + end +) + +dt.register_event(MODULE, "mouse-over-image-changed", + function(event, image) + if #dt.gui.selection() < 1 then + if image then + dt.gui.libs.image.set_sensitive(MODULE, true) + else + dt.gui.libs.image.set_sensitive(MODULE, false) + end + end + end +) + +return script_data From dbf0c5d853da0a3de0cd4877abf79a9e1a975e6e Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Sun, 9 Nov 2025 00:13:48 -0500 Subject: [PATCH 14/14] official/regenerate_thumbnails - added check to ensure job has not been canceled. --- official/regenerate_thumbnails.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/official/regenerate_thumbnails.lua b/official/regenerate_thumbnails.lua index cbef00c6..ffe1dafe 100644 --- a/official/regenerate_thumbnails.lua +++ b/official/regenerate_thumbnails.lua @@ -181,6 +181,9 @@ local function generate_thumbnails(images) image:drop_cache() image:generate_cache(true, 1, 3) if has_job then + if not rt.job.valid then + return + end if count % 10 == 0 then rt.job.percent = count / #images end