--[[ ext_proc.lua - export and process with an arbitrary binary Copyright (C) 2025 Florian Tham . Heavily based on gimp.lua and thus is Copyright (c) 2016 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 . ]] --[[ ext_proc - export images and process with an arbitrary binary This script provides another storage (export target) for darktable. Selected images are exporte in the specified format to temporary storage. The script given in executable_path is launched, possibly with the parameters from executable_args, followed by the list of exported files. After script execution the temporary files are removed. USAGE * require this script from your main lua file * select an image or images * in the export dialog select "external process" and select the format and bit depth for the exported image(s). Specify the full path to your script. * Specify the script parameters, if any. * Press "export" BUGS, COMMENTS, SUGGESTIONS * Send to Florian Tham, fgtham@gmail.com ]] local dt = require "darktable" local du = require "lib/dtutils" local df = require "lib/dtutils.file" local dtsys = require "lib/dtutils.system" local ext_proc_widget = nil local wg = {} local MODULE = "ext_proc" du.check_min_api_version("7.0.0", MODULE) -- from https://github.com/ChristianBirzer/darktable_extra_scripts/blob/main/HeliconFocus.lua -- find locale directory: local scriptfile = debug.getinfo( 1, "S" ) local localedir = dt.configuration.config_dir..'/lua/locale/' if scriptfile ~= nil and scriptfile.source ~= nil then local path = scriptfile.source:match( "[^@].*[/\\]" "]" ) localedir = path..os_path_seperator..'locale' end local gettext = dt.gettext gettext.bindtextdomain(MODULE, localedir) local function _(msgid) return gettext.dgettext(MODULE, msgid) end -- return data structure for script_manager local script_data = {} script_data.metadata = { name = _("external processing"), purpose = _("export and process with an arbitrary executable"), author = "Florian Tham ", help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/ext_proc" } 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, otherwise leave as nil 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 local function run_executable(storage, image_table, extra_data) --finalize local executable_path = wg.text_executable_path.text local executable_args = wg.text_executable_args.text local executable_exists = df.check_if_file_exists(executable_path) local has_exec_bit = df.test_file(executable_path, "x") if not executable_exists then dt.print(executable_path .. ": file not found") dt.print_error("file not found") return end if not has_exec_bit then dt.print(executable_path .. ": not executable") dt.print_error("file is not executable") return end dt.preferences.write(MODULE, "executable_path", "string", executable_path) dt.preferences.write(MODULE, "executable_args", "string", executable_args) -- list of exported images local img_list -- reset and create image list img_list = "" for _,exp_img in pairs(image_table) do exp_img = df.sanitize_filename(exp_img) img_list = img_list ..exp_img.. " " end dt.print(_("launching script...")) local cmdline = executable_path .. " " .. executable_args .. " " .. img_list dt.print_log(cmdline) dtsys.external_command(cmdline) dt.print(_("done")) for _,exp_img in pairs(image_table) do os.remove(exp_img) end end local function destroy() dt.destroy_storage(MODULE) end -- Register wg.text_executable_path = dt.new_widget("entry"){ text = dt.preferences.read(MODULE, "executable_path", "string"), placeholder = _("/path/to/executable"), tooltip = _("absolute path to the binary or script that should be called"), editable = true, } wg.text_executable_args = dt.new_widget("entry"){ text = dt.preferences.read(MODULE, "executable_args", "string"), tooltip = _("optional parameters to be passed to the executable"), editable = true, } local ext_proc_widget = dt.new_widget("box"){ orientation = "vertical", dt.new_widget("label"){label = _("absolute file path")}, wg.text_executable_path, dt.new_widget("label"){label = _("optional parameters")}, wg.text_executable_args, } dt.register_storage( MODULE, _("external processing"), nil, run_executable, nil, nil, ext_proc_widget ) -- script_data.destroy = destroy return script_data