From 61a55f71f58878382e74ea30a9fa79bb64f7fea8 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Tue, 7 Aug 2018 22:08:09 -0400 Subject: [PATCH] Created a cross platform script that calls an external executable. It also performs a service that some users might find useful, extracting the embedded jpeg image from a raw file, then importing and grouping it with the raw. Updated README.md to reflect the new file --- README.md | 1 + examples/multi_os.lua | 243 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 examples/multi_os.lua diff --git a/README.md b/README.md index 7fad112..a26eacc 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ api_version|Yes|LMW|Print the current API version gettextExample|Yes|LM|How to use translation hello_world|Yes|LMW|Prints hello world when darktable starts moduleExample|Yes|LMW|How to create a lighttable module +multi_os|No|LMW|How to create a cross platform script that calls an external executable preferenceExamples|Yes|LMW|How to use preferences in a script printExamples|Yes|LMW|How to use various print functions from a script running_os|Yes|LMW|Print out the running operating system diff --git a/examples/multi_os.lua b/examples/multi_os.lua new file mode 100644 index 0000000..0371768 --- /dev/null +++ b/examples/multi_os.lua @@ -0,0 +1,243 @@ +--[[ + This file is part of darktable, + copyright (c) 2017,2018 Bill Ferguson + + darktable 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. + + darktable 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 darktable. If not, see . +]] +--[[ + multi_os - an example script that runs on linux, MacOS, and Windows. + + multi_os is an example of how to write a script that will run on different + operating systems. It uses the lua-scripts libraries to lessen the amount + of code that needs to be written, as well as gaining access to tested + cross-platform routines. This script also performs a function that some + may find useful. It creates a button in the lighttable selected images module + that extracts the embedded jpeg image from a raw file, then imports it and groups + it with the raw file. A keyboard shortcut is also created. A key combination can + be assigned to the shortcut in the lua preferences and then the action can be invoked + by hovering over the image and pressing the key combination. + + ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT + * ufraw-batch - https://ufraw.sourceforge.net + MacOS - install with homebrew + + USAGE + * require this script from your main lua file + * start darktable, open prefreences, go to lua options + and update the executable location if you are running + Windows or MacOS, then restart darktable. + * select an image or images + * click the button to extract the jpeg + + BUGS, COMMENTS, SUGGESTIONS + * Send to Bill Ferguson, wpferguson@gmail.com + + CHANGES +]] + +--[[ + require "darktable" provides the interface to the darktable lua functions used to + interact with darktable +]] + +local dt = require "darktable" + +--[[ + require "lib/..." provides access to functions that have been pulled from various + scripts and consolidated into libraries. Using the libraries eliminates having to + recode common functions in every script. +]] + +local du = require "lib/dtutils" -- utilities +local df = require "lib/dtutils.file" -- file utilities +local dtsys = require "lib/dtutils.system" -- system utilities + +--[[ + darktable is an international program, and it's user interface has been translated into + many languages. The lua API provides gettext which is a function that looks for and replaces + strings with the translated equivalents based on locale. Even if you don't provide the + translations, inserting this lays the groundwork for anyone who wants to translate the strings. +]] + +local gettext = dt.gettext + +-- Tell gettext where to find the .mo file translating messages for a particular domain +gettext.bindtextdomain("multi_os",dt.configuration.config_dir.."/lua/locale/") + +local function _(msgid) + return gettext.dgettext("multi_os", msgid) +end + +--[[ + Specify the version(s) of the lua API that this script is compatible with. This script uses new + features released in the latest version of darktable and is only compatible with the latest API version. + Multiple API versions may be specified, seperated by commas. +]] + +dt.configuration.check_version(...,{5,0,0}) + +--[[ + copy_image_attributes is a local subroutine to copy image attributes in the database from the raw image + to the extracted jpeg image. Even though the subroutine could act on the variables in the main program + directly, passing them as arguments and using them in the subroutine is much safer. +]] + +local function copy_image_attributes(from, to, ...) + local args = {...} + if #args == 0 then + args[1] = "all" + end + if args[1] == "all" then + args[1] = "rating" + args[2] = "colors" + args[3] = "exif" + args[4] = "meta" + args[5] = "GPS" + end + for _,arg in ipairs(args) do + if arg == "rating" then + to.rating = from.rating + elseif arg == "colors" then + to.red = from.red + to.blue = from.blue + to.green = from.green + to.yellow = from.yellow + to.purple = from.purple + elseif arg == "exif" then + to.exif_maker = from.exif_maker + to.exif_model = from.exif_model + to.exif_lens = from.exif_lens + to.exif_aperture = from.exif_aperture + to.exif_exposure = from.exif_exposure + to.exif_focal_length = from.exif_focal_length + to.exif_iso = from.exif_iso + to.exif_datetime_taken = from.exif_datetime_taken + to.exif_focus_distance = from.exif_focus_distance + to.exif_crop = from.exif_crop + elseif arg == "GPS" then + to.elevation = from.elevation + to.longitude = from.longitude + to.latitude = from.latitude + elseif arg == "meta" then + to.publisher = from.publisher + to.title = from.title + to.creator = from.creator + to.rights = from.rights + to.description = from.description + else + dt.print_error("Unrecognized option to copy_image_attributes: " .. arg) + end + end +end + +--[[ + The main function called from the button or the shortcut. It takes one or more raw files, passed + in a table and extracts the embedded jpeg images +]] + +local function extract_embedded_jpeg(images) + + --[[ + check if the executable exists, since we can't do anything without it. check_if_bin_exists() + checks to see if there is a saved executable location. If not, it checks for the executable + in the user's path. When it finds the executable, it returns the command to run it. + ]] + + local ufraw_executable = df.check_if_bin_exists("ufraw-batch") + if ufraw_executable then + for _, image in ipairs(images) do + if image.is_raw then + local img_file = du.join({image.path, image.filename}, "/") + + --[[ + dtsys.external_command() is operating system aware and formats the command as necessary. + df.sanitize_filename() is used to quote the image filepath to protect from spaces. It is also + operating system aware and uses the corresponding quotes. + ]] + + if dtsys.external_command(ufraw_executable .. " --silent --embedded-image " .. df.sanitize_filename(img_file)) then + local jpg_img_file = df.chop_filetype(img_file) .. ".embedded.jpg" + dt.print_log("jpg_img_file set to ", jpg_img_file) -- print a debugging message + local myimage = dt.database.import(jpg_img_file) + myimage:group_with(image.group_leader) + copy_image_attributes(image, myimage, "all") + + --[[ + copy all of the tags except the darktable tags + ]] + + for _,tag in pairs(dt.tags.get_tags(image)) do + if not (string.sub(tag.name,1,9) == "darktable") then + dt.print_log("attaching tag") + dt.tags.attach(tag,myimage) + end + end + end + else + dt.print_error(image.filename .. " is not a raw file. No image can be extracted") -- print debugging error message + dt.print(image.filename .. " is not a raw file. No image can be extracted") -- print the error to the screen + end + end + else + dt.print_error("ufraw-batch not found. Exiting...") -- print debugging error message + dt.print("ufraw-batch not found. Exiting...") -- print the error to the screen + end +end + +--[[ + Windows and MacOS don't place executables in the user's path so their location needs to be specified + so that the script can find them. An exception to this is packages installed on MacOS with homebrew. Those + executables are put in /usr/local/bin. These are saved as executable path preferences. check_if_bin_exists() + looks for the executable path preference for the executable. If it doesn't find one, then the path is checked + to see if the executable is there. +]] + +local executable = "ufraw-batch" +local ufraw_batch_path_widget = dt.new_widget("file_chooser_button"){ + title = _("Select ufraw-batch[.exe] executable"), + value = df.get_executable_path_preference(executable), + is_directory = false, + changed_callback = function(self) + if df.check_if_bin_exists(self.value) then + df.set_executable_path_preference(executable, self.value) + end + end +} +dt.preferences.register("executable_paths", "ufraw-batch", -- name + "file", -- type + _('multi_os: ufraw-batch location'), -- label + _('Installed location of ufraw-batch. Requires restart to take effect.'), -- tooltip + "ufraw-batch", -- default + ufraw_batch_path_widget +) + +--[[ + Add a button to the selected images module in lighttable +]] + +dt.gui.libs.image.register_action( + _("extract embedded jpeg"), + function(event, images) extract_embedded_jpeg(images) end, + "extract embedded jpeg" +) + +--[[ + Add a shortcut +]] + +dt.register_event( + "shortcut", + function(event, shortcut) extract_embedded_jpeg(dt.gui.action_images) end, + "extract embedded jpeg" +)