Skip to content

Commit aec9ade

Browse files
committed
Variable substitution in output directory.
Force exporting files, but left the code to reconfigure it. Added more robust way to set export profile.
1 parent 0e97f29 commit aec9ade

File tree

3 files changed

+64
-38
lines changed

3 files changed

+64
-38
lines changed

birzer

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit fd7ac9dc6291a3e125d3df165b408534c4c89328

contrib/ultrahdr.lua

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ USAGE
3939
]] local dt = require "darktable"
4040
local du = require "lib/dtutils"
4141
local df = require "lib/dtutils.file"
42+
local ds = require "lib/dtutils.string"
4243
local log = require "lib/dtutils.log"
4344
local dtsys = require "lib/dtutils.system"
4445
local dd = require "lib/dtutils.debug"
@@ -65,8 +66,9 @@ local GUI = {
6566
encoding_settings_box = {},
6667
output_settings_label = {},
6768
output_settings_box = {},
68-
use_original_directory = {},
69-
output_directory_widget = {},
69+
output_filepath_label = {},
70+
output_filepath_widget = {},
71+
overwrite_on_conflict = {},
7072
copy_exif = {},
7173
import_to_darktable = {},
7274
min_content_boost = {},
@@ -110,6 +112,12 @@ local SELECTION_TYPE_GROUP_BY_FNAME = 2
110112
local DT_COLORSPACE_PQ_P3 = 24
111113
local DT_COLORSPACE_DISPLAY_P3 = 26
112114

115+
-- 1-based position of a colorspace in export profile combobox.
116+
local COLORSPACE_TO_GUI_ACTION = {
117+
[DT_COLORSPACE_PQ_P3] = 9,
118+
[DT_COLORSPACE_DISPLAY_P3] = 11
119+
}
120+
113121
local function generate_metadata_file(settings)
114122
local metadata_file_fmt = [[--maxContentBoost %f
115123
--minContentBoost %f
@@ -135,10 +143,8 @@ end
135143
local function save_preferences()
136144
dt.preferences.write(namespace, "encoding_variant", "integer", GUI.optionwidgets.encoding_variant_combo.selected)
137145
dt.preferences.write(namespace, "selection_type", "integer", GUI.optionwidgets.selection_type_combo.selected)
138-
dt.preferences.write(namespace, "use_original_directory", "bool", GUI.optionwidgets.use_original_directory.value)
139-
if GUI.optionwidgets.output_directory_widget.value then
140-
dt.preferences.write(namespace, "output_directory", "string", GUI.optionwidgets.output_directory_widget.value)
141-
end
146+
dt.preferences.write(namespace, "output_filepath_pattern", "string", GUI.optionwidgets.output_filepath_widget.text)
147+
dt.preferences.write(namespace, "overwrite_on_conflict", "bool", GUI.optionwidgets.overwrite_on_conflict.value)
142148
dt.preferences.write(namespace, "import_to_darktable", "bool", GUI.optionwidgets.import_to_darktable.value)
143149
dt.preferences.write(namespace, "copy_exif", "bool", GUI.optionwidgets.copy_exif.value)
144150
if GUI.optionwidgets.min_content_boost.value then
@@ -151,7 +157,7 @@ local function save_preferences()
151157
dt.preferences.write(namespace, "gainmap_downsampling", "integer",
152158
GUI.optionwidgets.gainmap_downsampling_widget.value)
153159
dt.preferences.write(namespace, "target_display_peak_nits", "integer",
154-
(GUI.optionwidgets.target_display_peak_nits_widget.value+0.5)//1)
160+
(GUI.optionwidgets.target_display_peak_nits_widget.value + 0.5) // 1)
155161

156162
end
157163

@@ -169,11 +175,8 @@ local function load_preferences()
169175
GUI.optionwidgets.selection_type_combo.selected = math.max(
170176
dt.preferences.read(namespace, "selection_type", "integer"), SELECTION_TYPE_ONE_STACK)
171177

172-
GUI.optionwidgets.output_directory_widget.value = dt.preferences.read(namespace, "output_directory", "string")
173-
GUI.optionwidgets.use_original_directory.value = dt.preferences.read(namespace, "use_original_directory", "bool")
174-
if not GUI.optionwidgets.output_directory_widget.value then
175-
GUI.optionwidgets.use_original_directory.value = true
176-
end
178+
GUI.optionwidgets.output_filepath_widget.text = dt.preferences.read(namespace, "output_filepath_pattern", "string")
179+
GUI.optionwidgets.overwrite_on_conflict.value = dt.preferences.read(namespace, "overwrite_on_conflict", "bool")
177180
GUI.optionwidgets.import_to_darktable.value = dt.preferences.read(namespace, "import_to_darktable", "bool")
178181
GUI.optionwidgets.copy_exif.value = dt.preferences.read(namespace, "copy_exif", "bool")
179182
GUI.optionwidgets.min_content_boost.value = default_to(dt.preferences.read(namespace, "min_content_boost", "float"),
@@ -191,10 +194,25 @@ local function load_preferences()
191194
dt.preferences.read(namespace, "gainmap_downsampling", "integer"), 0)
192195
end
193196

197+
local function set_profile(colorspace)
198+
local set_directly = true
199+
200+
if set_directly then
201+
-- New method, with hardcoded export profile values.
202+
local old = dt.gui.action("lib/export/profile", 0, "selection", "", "") * -1
203+
local new = COLORSPACE_TO_GUI_ACTION[colorspace] or colorspace
204+
log.msg(log.debug, string.format("%d %d %d %d", colorspace, new, old, new - old))
205+
dt.gui.action("lib/export/profile", 0, "selection", "next", new - old)
206+
return old
207+
else
208+
-- Old method, timing-dependent
209+
return set_combobox("lib/export/profile", 0, "plugins/lighttable/export/icctype", colorspace)
210+
end
211+
end
212+
194213
-- Changes the combobox selection blindly until a paired config value is set.
195214
-- Workaround for https://github.com/darktable-org/lua-scripts/issues/522
196215
local function set_combobox(path, instance, config_name, new_config_value)
197-
198216
local pref = dt.preferences.read("darktable", config_name, "integer")
199217
if pref == new_config_value then
200218
return new_config_value
@@ -223,8 +241,8 @@ local function assert_settings_correct(encoding_variant)
223241
exiftool = df.check_if_bin_exists("exiftool"),
224242
ffmpeg = df.check_if_bin_exists("ffmpeg")
225243
},
226-
output = GUI.optionwidgets.output_directory_widget.value,
227-
use_original_dir = GUI.optionwidgets.use_original_directory.value,
244+
overwrite_on_conflict = GUI.optionwidgets.overwrite_on_conflict.value,
245+
output_filepath_pattern = GUI.optionwidgets.output_filepath_widget.text,
228246
import_to_darktable = GUI.optionwidgets.import_to_darktable.value,
229247
copy_exif = GUI.optionwidgets.copy_exif.value,
230248
metadata = {
@@ -234,16 +252,13 @@ local function assert_settings_correct(encoding_variant)
234252
hdr_capacity_max = GUI.optionwidgets.hdr_capacity_max.value
235253
},
236254
quality = GUI.optionwidgets.quality_widget.value,
237-
target_display_peak_nits = (GUI.optionwidgets.target_display_peak_nits_widget.value+0.5)//1,
255+
target_display_peak_nits = (GUI.optionwidgets.target_display_peak_nits_widget.value + 0.5) // 1,
238256
downsample = 2 ^ GUI.optionwidgets.gainmap_downsampling_widget.value,
239257
tmpdir = dt.configuration.tmp_dir,
240-
skip_cleanup = false -- keep temporary files around, for debugging.
258+
skip_cleanup = false, -- keep temporary files around, for debugging.
259+
force_export = true -- if false, will copy source files instead of exporting if the file extension matches the format expectation.
241260
}
242261

243-
if not settings.use_original_dir and (not settings.output or not df.check_if_file_exists(settings.output)) then
244-
table.insert(errors, string.format(_("output directory (%s) not found"), settings.output))
245-
end
246-
247262
for k, v in pairs(settings.bin) do
248263
if not v then
249264
table.insert(errors, string.format(_("%s binary not found"), k))
@@ -387,10 +402,11 @@ local function generate_ultrahdr(encoding_variant, images, settings, step, total
387402
end
388403

389404
function copy_or_export(src_image, dest, format, colorspace, props)
390-
if df.get_filetype(src_image.filename) == df.get_filetype(dest) and not src_image.is_altered then
405+
if not settings.force_export and df.get_filetype(src_image.filename) == df.get_filetype(dest) and
406+
not src_image.is_altered then
391407
return df.file_copy(src_image.path .. PS .. src_image.filename, dest)
392408
else
393-
local prev = set_combobox("lib/export/profile", 0, "plugins/lighttable/export/icctype", colorspace)
409+
local prev = set_profile(colorspace)
394410
if not prev then
395411
return false
396412
end
@@ -405,7 +421,7 @@ local function generate_ultrahdr(encoding_variant, images, settings, step, total
405421
end
406422

407423
if prev then
408-
set_combobox("lib/export/profile", 0, "plugins/lighttable/export/icctype", prev)
424+
set_profile(prev)
409425
end
410426
return ok
411427
end
@@ -655,8 +671,10 @@ local function generate_ultrahdr(encoding_variant, images, settings, step, total
655671
update_job_progress()
656672
end
657673

658-
local output_dir = settings.use_original_dir and best_source_image.path or settings.output
659-
local output_file = df.create_unique_filename(output_dir .. PS .. df.get_filename(uhdr))
674+
local output_file = ds.substitute(best_source_image, step + 1, settings.output_filepath_pattern) .. ".jpg"
675+
if not settings.overwrite_on_conflict then
676+
output_file = df.create_unique_filename(output_file)
677+
end
660678
ok = df.file_move(uhdr, output_file)
661679
if not ok then
662680
table.insert(errors, string.format(_("Error generating UltraHDR for %s"), best_source_image.filename))
@@ -733,17 +751,20 @@ GUI.optionwidgets.output_settings_label = dt.new_widget("section_label") {
733751
label = _("output")
734752
}
735753

736-
GUI.optionwidgets.output_directory_widget = dt.new_widget("file_chooser_button") {
737-
title = _("select directory to write UltraHDR image files to"),
738-
is_directory = true
754+
GUI.optionwidgets.output_filepath_label = dt.new_widget("label") {
755+
label = _("file path pattern"),
756+
tooltip = ds.get_substitution_tooltip()
739757
}
740758

741-
GUI.optionwidgets.use_original_directory = dt.new_widget("check_button") {
742-
label = _("export to original directory"),
743-
tooltip = _("Write UltraHDR images to the same directory as their original images"),
744-
clicked_callback = function(self)
745-
GUI.optionwidgets.output_directory_widget.sensitive = not self.value
746-
end
759+
GUI.optionwidgets.output_filepath_widget = dt.new_widget("entry") {
760+
tooltip = ds.get_substitution_tooltip(),
761+
placeholder = _("e.g. $(FILE_FOLDER)/$(FILE_NAME)_ultrahdr")
762+
}
763+
764+
GUI.optionwidgets.overwrite_on_conflict = dt.new_widget("check_button") {
765+
label = _("overwrite if exists"),
766+
tooltip = _(
767+
"If the output file already exists, overwrite it. If unchecked, a unique filename will be created instead.")
747768
}
748769

749770
GUI.optionwidgets.import_to_darktable = dt.new_widget("check_button") {
@@ -759,8 +780,9 @@ GUI.optionwidgets.copy_exif = dt.new_widget("check_button") {
759780
GUI.optionwidgets.output_settings_box = dt.new_widget("box") {
760781
orientation = "vertical",
761782
GUI.optionwidgets.output_settings_label,
762-
GUI.optionwidgets.use_original_directory,
763-
GUI.optionwidgets.output_directory_widget,
783+
GUI.optionwidgets.output_filepath_label,
784+
GUI.optionwidgets.output_filepath_widget,
785+
GUI.optionwidgets.overwrite_on_conflict,
764786
GUI.optionwidgets.import_to_darktable,
765787
GUI.optionwidgets.copy_exif
766788
}
@@ -845,7 +867,9 @@ This will determine the method used to generate UltraHDR.
845867
846868
By default, the first image in a stack is treated as SDR, and the second one is a gain map/HDR.
847869
You can force the image into a specific stack slot by attaching "hdr" / "gainmap" tags to it.
848-
]]), _("SDR + gain map"), _("SDR + HDR"), _("SDR only"), _("HDR only")),
870+
871+
For HDR source images, apply a log2(203 nits/10000 nits) = -5.62 EV exposure correction
872+
before generating UltraHDR.]]), _("SDR + gain map"), _("SDR + HDR"), _("SDR only"), _("HDR only")),
849873
selected = 0,
850874
changed_callback = function(self)
851875
GUI.run.sensitive = self.selected and self.selected > 0

koto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../darktable-lua-scripts

0 commit comments

Comments
 (0)