diff --git a/contrib/ultrahdr.lua b/contrib/ultrahdr.lua index 381b013..d2540a3 100644 --- a/contrib/ultrahdr.lua +++ b/contrib/ultrahdr.lua @@ -17,8 +17,7 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . -]] ---[[ +]] --[[ ULTRAHDR Add a new storage option to generate UltraHDR JPG images. @@ -32,35 +31,59 @@ assuming the first one encountered is the base SDR image, and the second one is The images are merged using libultrahdr example application (ultrahdr_app). ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT -* ultrahdr_app (from https://github.com/google/libultrahdr example dir) +* ultrahdr_app (built using https://github.com/google/libultrahdr/blob/main/docs/building.md instructions) * exiftool +* ffmpeg USAGE * require this file from your main luarc config file -* set exiftool and libultrahdr_app tool paths +* set binary tool paths -This plugin will add a new storage option. - -]] -local dt = require "darktable" +]] local dt = require "darktable" local du = require "lib/dtutils" local df = require "lib/dtutils.file" local log = require "lib/dtutils.log" local dtsys = require "lib/dtutils.system" +local dd = require "lib/dtutils.debug" local gettext = dt.gettext.gettext -local namespace = 'module_ultrahdr' +local namespace = "module_ultrahdr" -- works with darktable API version from 5.0.0 on du.check_min_api_version("7.0.0", "ultrahdr") -dt.gettext.bindtextdomain("ultrahdr", dt.configuration.config_dir .. "/lua/locale/") +dt.gettext.bindtextdomain(namespace, dt.configuration.config_dir .. "/lua/locale/") local function _(msgid) return gettext(msgid) end --- return data structure for script_manager +local job + +local GUI = { + optionwidgets = { + settings_label = {}, + encoding_variant_combo = {}, + encoding_settings_box = {}, + output_settings_label = {}, + output_settings_box = {}, + use_original_directory = {}, + output_directory_widget = {}, + copy_exif = {}, + import_to_darktable = {}, + metadata_path_label = {}, + metadata_path_widget = {}, + metadata_path_box = {}, + edit_executables_button = {}, + executable_path_widget = {} + }, + options = {}, + run = {} +} + +local flags = {} +flags.event_registered = false -- keep track of whether we've added an event callback or not +flags.module_installed = false -- keep track of whether the module is module_installed local script_data = {} @@ -70,247 +93,449 @@ script_data.metadata = { author = "Krzysztof Kotowicz" } -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 PS = dt.configuration.running_os == "windows" and "\\" or "/" +local ENCODING_VARIANT_SDR_AND_GAINMAP = 1 +local ENCODING_VARIANT_SDR_AND_HDR = 2 -local function image_path(image) - return image.path .. "/" .. image.filename +local function save_preferences() + dt.preferences.write(namespace, "encoding_variant", "integer", GUI.optionwidgets.encoding_variant_combo.selected) + if GUI.optionwidgets.metadata_path_widget.value then + dt.preferences.write(namespace, "metadata_path", "string", GUI.optionwidgets.metadata_path_widget.value) + end + dt.preferences.write(namespace, "use_original_directory", "bool", GUI.optionwidgets.use_original_directory.value) + dt.preferences.write(namespace, "output_directory", "string", GUI.optionwidgets.output_directory_widget.value) + dt.preferences.write(namespace, "import_to_darktable", "bool", GUI.optionwidgets.import_to_darktable.value) + dt.preferences.write(namespace, "copy_exif", "bool", GUI.optionwidgets.copy_exif.value) end -local ENCODING_VARIANT_API_4 = 1 +local function load_preferences() + GUI.optionwidgets.encoding_variant_combo.selected = dt.preferences.read(namespace, "encoding_variant", "integer") or + ENCODING_VARIANT_SDR_AND_GAINMAP + GUI.optionwidgets.metadata_path_widget.value = dt.preferences.read(namespace, "metadata_path", "string") + GUI.optionwidgets.use_original_directory.value = dt.preferences.read(namespace, "use_original_directory", "bool") + GUI.optionwidgets.output_directory_widget.value = dt.preferences.read(namespace, "output_directory", "string") + GUI.optionwidgets.import_to_darktable.value = dt.preferences.read(namespace, "import_to_darktable", "bool") + GUI.optionwidgets.copy_exif.value = dt.preferences.read(namespace, "copy_exif", "bool") +end local function get_encoding_variant() - local encoding_variant = dt.preferences.read("ultrahdr", "encoding variant", "integer") - if not encoding_variant then - encoding_variant = ENCODING_VARIANT_API_4 - end - return encoding_variant + return GUI.optionwidgets.encoding_variant_combo.selected end -local function merge_ultrahdr_api4(base, gainmap, ultrahdr_app, exiftool, output) - local metadata = dt.preferences.read("ultrahdr", "metadata path", "string") - if not df.check_if_file_exists(metadata) then - dt.print(_("metadata file not found, did you set the correct path?")) - log.msg(log.error, "metadata file not found.") - return - end - local base_tmp = df.chop_filetype(base) .. ".tmp" - local hdr = df.chop_filetype(base) .. "_hdr." .. df.get_filetype(base) - dtsys.external_command(exiftool .. " -all= " .. df.sanitize_filename(base) .. " -o " .. - df.sanitize_filename(base_tmp)) - dtsys.external_command(exiftool .. " -all= " .. df.sanitize_filename(gainmap) .. " -overwrite_original") - dtsys.external_command(ultrahdr_app .. " -m 0 -i " .. df.sanitize_filename(base_tmp) .. " -g " .. - df.sanitize_filename(gainmap) .. " -f " .. df.sanitize_filename(metadata) .. " -z " .. - df.sanitize_filename(hdr)) - dtsys.external_command(exiftool .. " -tagsfromfile " .. df.sanitize_filename(base) .. " -all>all " .. - df.sanitize_filename(hdr) .. " -overwrite_original") - df.file_move(hdr, df.create_unique_filename(output .. "/" .. df.get_filename(hdr))) - os.remove(base) - os.remove(base_tmp) - os.remove(gainmap) -end +local function assert_settings_correct(encoding_variant) + local settings = { + bin = { + ultrahdr_app = df.check_if_bin_exists("ultrahdr_app"), + exiftool = df.check_if_bin_exists("exiftool"), + ffmpeg = df.check_if_bin_exists("ffmpeg") + }, + output = GUI.optionwidgets.output_directory_widget.value, + use_original_dir = GUI.optionwidgets.use_original_directory.value, + import_to_darktable = GUI.optionwidgets.import_to_darktable.value, + copy_exif = GUI.optionwidgets.copy_exif.value, + metadata = GUI.optionwidgets.metadata_path_widget.value, + tmpdir = dt.configuration.tmp_dir + } -local function assert_settings_correct() - local ultrahdr_app = df.check_if_bin_exists("ultrahdr_app") - log.msg(log.debug, "ultrahdr_app set to ", ultrahdr_app) - local exiftool = df.check_if_bin_exists("exiftool") - log.msg(log.debug, "exiftool set to ", exiftool) - local output = dt.preferences.read("ultrahdr", "output dir", "string") - log.msg(log.debug, "output dir set to ", output) - - if not ultrahdr_app then - dt.print(_("ultrahdr_app is not found, did you set the path?")) - log.msg(log.error, "ultrahdr_app executable not found. Check if the executable is installed.") - log.msg(log.error, "If the executable is installed, check that the path is set correctly.") + if not settings.use_original_dir and not df.check_if_file_exists(settings.output) then + dt.print(string.format(_("output directory (%s) not found, did you set the correct path?"), settings.output)) return end - if not exiftool then - dt.print(_("exiftool is not found, did you set the path?")) - log.msg(log.error, "exiftool executable not found. Check if the executable is installed.") - log.msg(log.error, "If the executable is installed, check that the path is set correctly.") - return + for k, v in pairs(settings.bin) do + if not v then + dt.print(string.format(_("%s is not found, did you set the path?"), k)) + return + end end - return ultrahdr_app, exiftool, output + if encoding_variant == ENCODING_VARIANT_SDR_AND_GAINMAP then + if not df.check_if_file_exists(settings.metadata) then + dt.print(_("metadata file not found, did you set the correct path?")) + log.msg(log.error, "metadata file not found.") + return + end + end + + return settings end -local function create_hdr(storage, image_table, extra_data) -- finalize +local function get_stacks(images, encoding_variant) + local stacks = {} + local extra_image_content_type, extra_image_extension + if encoding_variant == ENCODING_VARIANT_SDR_AND_GAINMAP then + extra_image_content_type = "gainmap" + elseif encoding_variant == ENCODING_VARIANT_SDR_AND_HDR then + extra_image_extension = "jxl" + extra_image_content_type = "hdr" + end + + local tags = nil + -- Group images into sdr, extra pairs based on their original filename, ignoring the extension + -- Assume that the first encountered image from each filename is an sdr one, unless it has a tag matching the expected extra_image_type, or has the expected extension + for k, v in pairs(images) do + local is_extra = false + tags = dt.tags.get_tags(v) + for ignore, tag in pairs(tags) do + if tag.name == extra_image_content_type then + is_extra = true + end + end + if extra_image_extension and df.get_filetype(v.filename) == extra_image_extension then + is_extra = true + end + -- we assume every image in the stack is generated from the same source image file + local key = df.chop_filetype(v.path .. PS .. v.filename) + if stacks[key] == nil then + stacks[key] = {} + end + if stacks[key]["sdr"] or is_extra then + stacks[key][extra_image_content_type] = v + else + stacks[key]["sdr"] = v + end + end + -- remove invalid stacks + local count = 0 + for k, v in pairs(stacks) do + if not v["sdr"] or not v[extra_image_content_type] then + stacks[k] = nil + elseif (v["sdr"].final_width ~= v[extra_image_content_type].final_width) or + (v["sdr"].final_height ~= v[extra_image_content_type].final_height) then + stacks[k] = nil + elseif extra_image_extension and df.get_filetype(v[extra_image_content_type].filename) ~= extra_image_extension then + stacks[k] = nil + else + count = count + 1 + end + end + return stacks, count +end + +local function stop_job(job) + job.valid = false +end + +local function execute_cmd(cmd) + log.msg(log.debug, cmd) + return dtsys.external_command(cmd) +end + +local function generate_ultrahdr(encoding_variant, images, settings, step, total_steps) + local total_substeps + local substep = 0 + local uhdr + + function update_job_progress() + substep = substep + 1 + if substep > total_substeps then + log.msg(log.debug, + string.format("total_substeps count is too low for encoding_variant %d", encoding_variant)) + end + job.percent = (total_substeps * step + substep) / (total_steps * total_substeps) + end + + if encoding_variant == ENCODING_VARIANT_SDR_AND_GAINMAP then + total_substeps = 6 + local msg = string.format(_("Stacking %s"), images["sdr"].filename) + log.msg(log.info, msg) + dt.print(msg) + -- Export both SDR and gainmap to JPEGs + local exporter = dt.new_format("jpeg") + exporter.quality = 95 + local sdr = df.create_unique_filename(settings.tmpdir .. PS .. df.chop_filetype(images["sdr"].filename) .. + ".jpg") + exporter:write_image(images["sdr"], sdr) + local gainmap = df.create_unique_filename(settings.tmpdir .. PS .. images["gainmap"].filename .. "_gainmap.jpg") + exporter:write_image(images["gainmap"], gainmap) + + log.msg(log.debug, string.format(_("Exported files: %s, %s"), sdr, gainmap)) + update_job_progress() + -- Strip EXIFs + execute_cmd(settings.bin.exiftool .. " -all= " .. df.sanitize_filename(sdr) .. " -o " .. + df.sanitize_filename(sdr .. ".noexif")) + execute_cmd(settings.bin.exiftool .. " -all= " .. df.sanitize_filename(gainmap) .. " -overwrite_original") + update_job_progress() + -- Merge files + uhdr = df.chop_filetype(sdr) .. "_ultrahdr.jpg" + + execute_cmd(settings.bin.ultrahdr_app .. " -m 0 -M 0 -i " .. df.sanitize_filename(sdr .. ".noexif") .. " -g " .. + df.sanitize_filename(gainmap) .. " -f " .. df.sanitize_filename(settings.metadata) .. " -z " .. + df.sanitize_filename(uhdr)) + update_job_progress() + -- Copy SDR's EXIF to UltraHDR file + if settings.copy_exif then + execute_cmd(settings.bin.exiftool .. " -tagsfromfile " .. df.sanitize_filename(sdr) .. " -all>all " .. + df.sanitize_filename(uhdr) .. " -overwrite_original -preserve") + end + update_job_progress() + -- Cleanup + os.remove(sdr) + os.remove(sdr .. ".noexif") + os.remove(gainmap) + update_job_progress() + elseif encoding_variant == ENCODING_VARIANT_SDR_AND_HDR then + total_substeps = 5 + -- https://discuss.pixls.us/t/manual-creation-of-ultrahdr-images/45004/20 + -- Step 1: Export SDR to PNG (HDR is already a JPEG-XL) + local exporter = dt.new_format("png") + exporter.bpp = 8 + local sdr = df.create_unique_filename(settings.tmpdir .. PS .. df.chop_filetype(images["sdr"].filename) .. + ".png") + exporter:write_image(images["sdr"], sdr) + uhdr = df.chop_filetype(sdr) .. "_ultrahdr.jpg" + + update_job_progress() + local extra = df.create_unique_filename(settings.tmpdir .. PS .. images["hdr"].filename .. ".raw") + + -- Step 3: Generate libultrahdr RAW images + execute_cmd(settings.bin.ffmpeg .. " -i " .. df.sanitize_filename(sdr) .. " -pix_fmt rgba -f rawvideo " .. + df.sanitize_filename(sdr .. ".raw")) + execute_cmd(settings.bin.ffmpeg .. " -i " .. + df.sanitize_filename(images["hdr"].path .. PS .. images["hdr"].filename) .. + " -pix_fmt p010le -f rawvideo " .. df.sanitize_filename(extra)) + update_job_progress() + execute_cmd(settings.bin.ultrahdr_app .. " -m 0 -y " .. df.sanitize_filename(sdr .. ".raw") .. " -p " .. + df.sanitize_filename(extra) .. " -a 0 -b 3 -c 1 -C 1 -t 2 -M 1 -s 1 -q 100 -Q 100 -D 1 " .. + " -w " .. tostring(images["sdr"].final_width) .. " -h " .. tostring(images["sdr"].final_height) .. + " -z " .. df.sanitize_filename(uhdr)) + update_job_progress() + if settings.copy_exif then + execute_cmd(settings.bin.exiftool .. " -tagsfromfile " .. df.sanitize_filename(sdr) .. " -all>all " .. + df.sanitize_filename(uhdr) .. " -overwrite_original -preserve") + end + -- Cleanup + os.remove(sdr) + os.remove(sdr .. ".raw") + os.remove(extra) + update_job_progress() + end + + local output_dir = settings.use_original_dir and images["sdr"].path or settings.output + local output_file = df.create_unique_filename(output_dir .. PS .. df.get_filename(uhdr)) + df.file_move(uhdr, output_file) + if settings.import_to_darktable then + local img = dt.database.import(output_file) + -- Add "ultrahdr" tag to the imported image + local tagnr = dt.tags.find("ultrahdr") + if tagnr == nil then + dt.tags.create("ultrahdr") + tagnr = dt.tags.find("ultrahdr") + end + dt.tags.attach(tagnr, img) + end + update_job_progress() +end + +local function main() local saved_log_level = log.log_level() log.log_level(log.info) - local ultrahdr_app, exiftool, output = assert_settings_correct() - if not ultrahdr_app or not exiftool then - return - end + save_preferences() + local encoding_variant = get_encoding_variant() log.msg(log.info, string.format("using encoding variant %d", encoding_variant)) - if encoding_variant == ENCODING_VARIANT_API_4 then - local merged = 0 - for ignore, v in pairs(extra_data) do - if not v then - goto continue - end - local msg = string.format(_("Merging %s and %s"), df.get_filename(image_table[v["base"]]), - df.get_filename(image_table[v["gainmap"]])) - log.msg(log.info, msg) - dt.print(msg) - merge_ultrahdr_api4(image_table[v["base"]], image_table[v["gainmap"]], ultrahdr_app, exiftool, output) - merged = merged + 1 - ::continue:: - end - for ignore, v in pairs(image_table) do - if df.check_if_file_exists(v) then os.remove(v) end - end - dt.print(string.format(_("Created %d UltraHDR image(s) in %s"), merged, output)) - else - local msg = string.format(_("Unknown encoding variant: %d"), encoding_variant) - dt.print_error(msg) - dt.print(msg) + + local settings = assert_settings_correct(encoding_variant) + if not settings then + dt.print(_("Export settings are incorrect, exiting...")) + log.log_level(saved_log_level) + return end + + local images = dt.gui.selection() -- get selected images + if #images < 2 then + dt.print(_("Select at least 2 images to generate UltraHDR image")) + log.log_level(saved_log_level) + return + end + + local stacks, stack_count = get_stacks(images, encoding_variant) + dt.print(string.format(_("Detected %d image stack(s)"), stack_count)) + if stack_count == 0 then + log.log_level(saved_log_level) + return + end + job = dt.gui.create_job(_("Generating UltraHDR images"), true, stop_job) + local count = 0 + for i, v in pairs(stacks) do + generate_ultrahdr(encoding_variant, v, settings, count, stack_count) + count = count + 1 + -- sleep for a short moment to give stop_job callback function a chance to run + dt.control.sleep(10) + end + -- stop job and remove progress_bar from ui, but only if not alreay canceled + if (job.valid) then + job.valid = false + end + log.log_level(saved_log_level) end +GUI.optionwidgets.settings_label = dt.new_widget("section_label") { + label = _("UltraHDR settings") +} + +GUI.optionwidgets.output_settings_label = dt.new_widget("section_label") { + label = _("Output") +} + +GUI.optionwidgets.output_directory_widget = dt.new_widget("file_chooser_button") { + title = _("Select directory to write UltraHDR image files to"), + is_directory = true +} + +GUI.optionwidgets.use_original_directory = dt.new_widget("check_button") { + label = _("Export to original directory"), + tooltip = _("Write UltraHDR images to the same directory as their original images"), + clicked_callback = function(self) + GUI.optionwidgets.output_directory_widget.sensitive = not self.value + end +} + +GUI.optionwidgets.import_to_darktable = dt.new_widget("check_button") { + label = _("Import UltraHDRs to Darktable"), + tooltip = _("Import UltraHDR images to Darktable library after generating, with an 'ultrahdr' tag attached.") +} + +GUI.optionwidgets.copy_exif = dt.new_widget("check_button") { + label = _("Copy EXIF data from SDR file(s)"), + tooltip = _("Copy EXIF data into UltraHDR file(s) from their SDR sources.") +} + +GUI.optionwidgets.output_settings_box = dt.new_widget("box") { + orientation = "vertical", + GUI.optionwidgets.output_settings_label, + GUI.optionwidgets.use_original_directory, + GUI.optionwidgets.output_directory_widget, + GUI.optionwidgets.import_to_darktable, + GUI.optionwidgets.copy_exif +} + +GUI.optionwidgets.metadata_path_label = dt.new_widget("label") { + label = _("ultrahdr_app metadata.cfg file") +} + +GUI.optionwidgets.metadata_path_widget = dt.new_widget("file_chooser_button") { + title = "select libultrahdr metadata path", + is_directory = false +} + +GUI.optionwidgets.metadata_path_box = dt.new_widget("box") { + orientation = "vertical", + GUI.optionwidgets.metadata_path_label, + GUI.optionwidgets.metadata_path_widget +} + +GUI.optionwidgets.encoding_variant_combo = dt.new_widget("combobox") { + label = _("Source images"), + tooltip = string.format(_([[Select types of images in the selection. + +%s: SDR image paired with a monochromatic gain map image +%s: SDR image paired with a JPEG-XL HDR image (10-bit, 'PQ P3 RGB' profile recommended) + +UltraHDR image will be created for each pair of images that: + - have the same underlying image path + filename (ignoring file extension) + - have the same dimensions + + It is assumed that the first image in a pair is the SDR , unless it has a "hdr" / "gainmap" tag. +]]), _("SDR + monochrome gainmap"), _("SDR + JPEG-XL HDR")), + selected = 0, + changed_callback = function(self) + if self.selected == ENCODING_VARIANT_SDR_AND_GAINMAP then + GUI.optionwidgets.metadata_path_box.visible = true + else + GUI.optionwidgets.metadata_path_box.visible = false + end + end, + _("SDR + monochrome gainmap"), -- ENCODING_VARIANT_SDR_AND_GAINMAP, + _("SDR + JPEG-XL HDR") -- ENCODING_VARIANT_SDR_AND_HDR, +} + +GUI.optionwidgets.encoding_settings_box = dt.new_widget("box") { + orientation = "vertical", + GUI.optionwidgets.encoding_variant_combo, + GUI.optionwidgets.metadata_path_box +} + +GUI.optionwidgets.executable_path_widget = df.executable_path_widget({"ultrahdr_app", "exiftool", "ffmpeg"}) +GUI.optionwidgets.executable_path_widget.visible = false + +GUI.optionwidgets.edit_executables_button = dt.new_widget("button") { + label = _("Show / hide executables"), + tooltip = _("Show / hide settings for executable files required for the plugin functionality"), + clicked_callback = function() + GUI.optionwidgets.executable_path_widget.visible = not GUI.optionwidgets.executable_path_widget.visible + end +} + +GUI.options = dt.new_widget("box") { + orientation = "vertical", + GUI.optionwidgets.settings_label, + GUI.optionwidgets.encoding_settings_box, + GUI.optionwidgets.edit_executables_button, + GUI.optionwidgets.executable_path_widget, + GUI.optionwidgets.output_settings_box +} + +GUI.run = dt.new_widget("button") { + label = _("Generate UltraHDR"), + tooltip = _([[Generate UltraHDR image(s) from selection + +Global options in the export module apply to the SDR image. Make sure that a proper color 'profile' setting is used (e.g. Display P3) +]]), + clicked_callback = main +} + +load_preferences() + +local function install_module() + if flags.module_installed then + return + end + dt.register_lib( -- register module + namespace, -- Module name + _("UltraHDR"), -- name + true, -- expandable + true, -- resetable + { + [dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 99} + }, -- containers + dt.new_widget("box") { + orientation = "vertical", + GUI.options, + GUI.run + }, nil, -- view_enter + nil -- view_leave + ) +end + local function destroy() - dt.destroy_storage(namespace) + dt.gui.libs[namespace].visible = false end - - -local function is_supported(storage, format) - local encoding_variant = get_encoding_variant() - if encoding_variant == ENCODING_VARIANT_API_4 then - -- API-4 expects compressed base and gainmap - -- https://github.com/google/libultrahdr/tree/main?tab=readme-ov-file#encoding-api-outline - return format.extension == "jpg" - end - return false +local function restart() + dt.gui.libs[namespace].visible = true end -local function initialize(storage, format, images, high_quality, extra_data) - local saved_log_level = log.log_level() - log.log_level(log.info) - local tags = nil - -- Group images into base, gainmap pairs based on their original filename. - -- Assume that the first encountered image from each filename is a base one, unless it has a "gainmap" tag. - for k, v in pairs(images) do - local has_gainmap_tag = false - tags = dt.tags.get_tags(v) - for ignore, tag in pairs(tags) do - if tag.name == "gainmap" then - has_gainmap_tag = true +if dt.gui.current_view().id == "lighttable" then -- make sure we are in lighttable view + install_module() -- register the lib +else + if not flags.event_registered then -- if we are not in lighttable view then register an event to signal when we might be + -- https://www.darktable.org/lua-api/index.html#darktable_register_event + dt.register_event(namespace, "view-changed", -- we want to be informed when the view changes + function(event, old_view, new_view) + if new_view.name == "lighttable" and old_view.name == "darkroom" then -- if the view changes from darkroom to lighttable + install_module() -- register the lib end - end - local key = image_path(v) - if extra_data[key] == nil then - extra_data[key] = {} - end - if extra_data[key]["base"] or has_gainmap_tag then - extra_data[key]["gainmap"] = v - else - extra_data[key]["base"] = v - end + end) + flags.event_registered = true -- keep track of whether we have an event handler installed end - -- remove incomplete entries - for k, v in pairs(extra_data) do - if not v["base"] or not v["gainmap"] then - extra_data[k] = nil - end - end - log.log_level(saved_log_level) - return nil end -local function metadata_file_widget() - local box_widgets = {} - table.insert(box_widgets, dt.new_widget("label") { - label = "libultrahdr metadata file" - }) - local path = dt.preferences.read("ultrahdr", "metadata path", "string") - if not path then - path = "" - end - table.insert(box_widgets, dt.new_widget("file_chooser_button") { - title = "select libultrahdr metadata path", - value = path, - is_directory = false, - changed_callback = function(self) - if df.check_if_file_exists(self.value) then - dt.preferences.write("ultrahdr", "metadata path", "string", self.value) - end - end - }) - - local box = dt.new_widget("box") { - orientation = "vertical", - table.unpack(box_widgets) - } - return box -end - -local function output_directory_widget() - local box_widgets = {} - table.insert(box_widgets, dt.new_widget("label") { - label = "output directory" - }) - local path = dt.preferences.read("ultrahdr", "output dir", "string") - if not path then - path = "" - end - table.insert(box_widgets, dt.new_widget("file_chooser_button") { - title = "select libultrahdr metadata path", - value = path, - is_directory = true, - changed_callback = function(self) - dt.preferences.write("ultrahdr", "output dir", "string", self.value) - end - }) - - local box = dt.new_widget("box") { - orientation = "vertical", - table.unpack(box_widgets) - } - return box -end - -local function encoding_variant_widget() - local metadata_widget = metadata_file_widget() - local encoding_variant = get_encoding_variant() - local combobox = dt.new_widget("combobox"){ - label = _("Generate HDR from"), - selected = encoding_variant, - changed_callback=function(self) - dt.preferences.write("ultrahdr", "encoding variant", "integer", self.selected) - if self.selected == ENCODING_VARIANT_API_4 then - metadata_widget.visible = true - else - metadata_widget.visible = false - end - end, - _("SDR + gainmap (API-4)") -- ENCODING_VARIANT_API_4, - } - - combobox.changed_callback(combobox) - return dt.new_widget("box") { - orientation = "vertical", - combobox, - metadata_widget - } -end - -local ultrahdr_widget = dt.new_widget("box") { - orientation = "vertical", - output_directory_widget(), - encoding_variant_widget(), - df.executable_path_widget({"ultrahdr_app", "exiftool"}) -} - --- Register - -dt.register_storage(namespace, _("UltraHDR JPEG"), nil, create_hdr, is_supported, initialize, ultrahdr_widget) - script_data.destroy = destroy +script_data.restart = restart +script_data.destroy_method = "hide" +script_data.show = restart return script_data