Created
May 24, 2023 20:15
-
-
Save vredesbyyrd/b826e876763eb39337a1dc4b7c13ab10 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
This file is part of darktable, | |
copyright (c)2021 Bill Ferguson <wpferguson@gmail.com> | |
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 <http://www.gnu.org/licenses/>. | |
]] | |
--[[ | |
metadata_manager.lua - a tool for adding additional metadata to the image information | |
metadata_manager allows a user to extend the image information display by adding | |
additional Exif tags to query and display. The Exif tags are those used by exiv2. | |
To see what tags are available, run "exiv2 -PE <image file>". A list of the Exif tags | |
used by the image are printed along with their values. | |
USAGE | |
* start metadata_manager | |
* specify a display name for the information in the metadata display name field | |
* enter the Exif tag for the information in the exiv2 metadata tag field | |
* click the create metadata information button | |
* repeat as many times as necesssary. | |
* select the names of the metadata you wish to activate | |
* click the activate button to add them to the image information display | |
and start monitoring which image the mouse is over. The image information | |
display will update every time a new image is hovered over | |
* To remove metadata fields from the image information, select the names to | |
deactivate and click the deactivate button. | |
* To remove an item from the list, select it by itself and click the remove from | |
list button. | |
ADDITIONAL SOFTWARE REQUIRED | |
exiv2 | |
]] | |
local dt = require "darktable" | |
local du = require "lib/dtutils" | |
local df = require "lib/dtutils.file" | |
local ds = require "lib/dtutils.string" | |
local log = require "lib/dtutils.log" | |
local function dummy(str) | |
return str | |
end | |
--ds.sanitize_lua = dummy | |
local metadata_manager = {} | |
local mm = metadata_manager | |
mm.event_registered = false | |
mm.module_installed = false | |
mm.cache = "" | |
mm.widgets = {} | |
mm.metadata_widgets = {} | |
mm.metadata_pairs = {} | |
mm.metadata = {} | |
mm.callbacks = {} | |
mm.query_string = " -PEkt" | |
mm.exiv2 = df.check_if_bin_exists("exiv2") | |
mm.cache_active = false | |
mm.last_image = nil | |
mm.run = false | |
local log_level = log.log_level() | |
log.log_level(log.debug) | |
local function save_preferences() | |
local pref = "" | |
for _, md in ipairs(mm.metadata_pairs) do | |
if #pref > 5 then | |
pref = string.format("%s,", pref) | |
end | |
pref = string.format("%s{%s,%s,%s,%s,%s}", pref, md.name, md.tag, tostring(md.selected), tostring(md.registered), md.query) | |
end | |
log.msg(log.info, "saving pref string " .. pref) | |
dt.preferences.write("metadata_manager", "mdpairs", "string", pref) | |
end | |
local function update_query_string() | |
local query_string = " -PEkt" | |
for _, md in ipairs(mm.metadata_pairs) do | |
if md.registered then | |
query_string = query_string .. md.query | |
end | |
end | |
mm.query_string = query_string | |
end | |
local function update_cache(image) | |
if image ~= mm.last_image then | |
log.msg(log.debug, "generating cache for new image ") | |
--log.msg(log.debug, "new image id is " .. tostring(image)) | |
if mm.exiv2 then | |
if mm.query_string and image then | |
--local cmd = mm.exiv2 .. mm.query_string .. " " .. string.format('%q', image.path) .. "/" .. image.filename | |
local cmd = string.format('%s%s %q', mm.exiv2, mm.query_string, image.path .. "/" .. image.filename) | |
log.msg(log.debug, "cmd is " .. cmd) | |
local p = io.popen(cmd) | |
if p then | |
mm.cache = p:read("*all") | |
p:close() | |
mm.last_image = image | |
else | |
log.msg(log.error, "exiv2 encountered a problem and could not read the file") | |
end | |
else | |
log.msg(log.warn, "no keys to query, not updating...") | |
end | |
else | |
log.msg(log.error, "exiv2 executable not found") | |
end | |
end | |
end | |
local function create_callback(key) | |
return function(image) | |
local result = "-" | |
update_cache(image) | |
local match_string = ds.sanitize_lua(key) .. "%s+(.-)\n" | |
log.msg(log.debug, "mm.cache is " .. mm.cache .. " and match_string is " .. match_string) | |
if mm.cache then | |
local a = string.match(mm.cache, match_string) | |
if a then | |
result = a | |
end | |
end | |
log.msg(log.debug, "returning result " .. result) | |
return result | |
end | |
end | |
local function add_metadata_pair(name, tag) | |
table.insert(mm.metadata_pairs, {name = name, tag = tag, callback = create_callback(tag), selected = true, registered = false, query = " -g " .. tag}) | |
end | |
local function remove_metadata_pair(selected) | |
-- unload the box from the bottom so the widgets don't change | |
-- position while you are deleting | |
for i = #mm.widgets.metadata_box, 1, -1 do | |
log.msg(log.debug, "clearing widget " .. i) | |
mm.widgets.metadata_box[i] = nil | |
end | |
for _, name in ipairs(selected) do | |
for i, md in ipairs(mm.metadata_pairs) do | |
if string.match(md.name, ds.sanitize_lua(name)) then | |
if md.registered then | |
log.msg(log.info, "removing " .. md.name .. " from image information") | |
dt.gui.libs.metadata_view.destroy_info(md.name) | |
end | |
table.remove(mm.metadata_pairs, i) | |
break | |
end | |
end | |
for i, widget in ipairs(mm.metadata_widgets) do | |
if string.match(widget.label, ds.sanitize_lua(name)) then | |
table.remove(mm.metadata_widgets, i) -- we should destroy the widget but there's not a function to do that... yet | |
break | |
end | |
end | |
end | |
log.msg(log.debug, "reloading metadata choices") | |
for i, widget in ipairs(mm.metadata_widgets) do | |
table.insert(mm.widgets.metadata_box, widget) | |
end | |
update_query_string() | |
save_preferences() | |
end | |
local function clear_image_info() | |
log.msg(log.info, "clearing all lua widgets from image information") | |
for _, md in ipairs(mm.metadata_pairs) do | |
if md.registered then | |
dt.gui.libs.metadata_view.destroy_info(md.name) | |
md.registered = false | |
end | |
end | |
log.msg(log.debug, "reset query string") | |
mm.query_string = " -PEkt" | |
end | |
local function load_image_info() | |
log.msg(log.info, "adding lua widgets to image information") | |
local loaded = false | |
for _, md in ipairs(mm.metadata_pairs) do | |
if md.registered then | |
log.msg(log.debug, "adding " .. md.name) | |
dt.gui.libs.metadata_view.register_info(md.name, md.callback) | |
loaded = true | |
end | |
end | |
update_query_string() | |
end | |
local function update_image_info(add) | |
local loaded = false | |
for _, md in ipairs(mm.metadata_pairs) do | |
if md.selected then | |
if add then | |
if not md.registered then | |
log.msg(log.debug, "adding " .. md.name .. " to image information display") | |
dt.gui.libs.metadata_view.register_info(md.name, md.callback) | |
md.registered = true | |
loaded = true | |
end | |
else -- remove | |
log.msg(log.debug, "removing " .. md.name .. " from image information display") | |
dt.gui.libs.metadata_view.destroy_info(md.name) | |
md.registered = false | |
end | |
end | |
end | |
update_query_string() | |
save_preferences() | |
end | |
local function update_button_sensitivity() | |
local count = 0 | |
local selected = 0 | |
for _, widget in ipairs(mm.metadata_widgets) do | |
count = count + 1 | |
if widget.value then | |
selected = selected + 1 | |
end | |
end | |
log.msg(log.debug, selected .. " buttons are selected of " .. count) | |
if selected > 0 then | |
mm.widgets.select_none.sensitive = true | |
mm.widgets.invert_selection.sensitive = true | |
mm.widgets.activate.sensitive = true | |
mm.widgets.deactivate.sensitive = true | |
mm.widgets.remove.sensitive = true | |
else | |
mm.widgets.select_none.sensitive = false | |
mm.widgets.invert_selection.sensitive = false | |
mm.widgets.activate.sensitive = false | |
mm.widgets.deactivate.sensitive = false | |
mm.widgets.remove.sensitive = false | |
end | |
if selected == count then | |
mm.widgets.select_all.sensitive = false | |
else | |
mm.widgets.select_all.sensitive = true | |
end | |
end | |
local function create_new_metadata_selector(name, tag, selected) | |
local sel = true | |
if selected ~= nil then | |
sel = selected | |
end | |
local widget = dt.new_widget("check_button"){ | |
label = name, | |
tooltip = tag, | |
value = true, | |
clicked_callback = function(this) | |
if mm.run then | |
for _, md in ipairs(mm.metadata_pairs) do | |
if string.match(this.label, ds.sanitize_lua(md.name)) then | |
md.selected = this.value | |
end | |
end | |
save_preferences() | |
update_button_sensitivity() | |
end | |
end | |
} | |
widget.value = sel | |
return widget | |
end | |
local function load_preferences() | |
local pref = dt.preferences.read("metadata_manager", "mdpairs", "string") | |
log.msg(log.debug, "got pref string " .. pref) | |
if #pref > 4 then | |
local e = -1 | |
local s = true | |
local str = "" | |
while(s) do | |
s, e, str = string.find(pref, "({.-})", e + 1) | |
if s then | |
str = str.gsub(str, "[{}]", "") | |
local el = du.split(str, ",") | |
if string.match(el[3], "true") then | |
el[3] = true | |
else | |
el[3] = false | |
end | |
if string.match(el[4], "true") then | |
el[4] = true | |
else | |
el[4] = false | |
end | |
table.insert(mm.metadata_pairs, {name = el[1], tag = el[2], selected = el[3], registered = el[4], callback = create_callback(el[2]), query = el[5]}) | |
table.insert(mm.metadata_widgets, create_new_metadata_selector(el[1], el[2], el[3])) | |
table.insert(mm.widgets.metadata_box, mm.metadata_widgets[#mm.metadata_widgets]) | |
end | |
end | |
load_image_info() | |
end | |
end | |
local function install_module() | |
if not mm.module_installed then | |
dt.register_lib( | |
"metadata_manager", -- Module name | |
"metadata manager", -- Visible name | |
true, -- expandable | |
false, -- resetable | |
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 0}}, -- containers | |
mm.widgets.main_box, | |
nil,-- view_enter | |
nil -- view_leave | |
) | |
mm.module_installed = true | |
end | |
load_preferences() | |
mm.run = true | |
end | |
local function destroy() | |
save_preferences() | |
clear_image_info() | |
dt.gui.libs.metadata_manager.visible = false | |
end | |
local function restart() | |
load_preferences() | |
dt.gui.libs.metadata_manager.visible = false | |
load_image_info() | |
end | |
mm.widgets.metadata_name = dt.new_widget("entry"){ | |
tooltip = "enter name to display for metadata", | |
placeholder = "enter display name for metadata", | |
text = "", | |
} | |
mm.widgets.metadata_tag = dt.new_widget("entry"){ | |
tooltip = "enter exiv2 tag for the metadata", | |
placeholder = "enter exiv2 tag for the metadata", | |
text = "" | |
} | |
mm.widgets.create_metadata = dt.new_widget("button"){ | |
label = "create metadata information", | |
tooltip = "add a metadata name and tag for selection", | |
clicked_callback = function(this) | |
add_metadata_pair(mm.widgets.metadata_name.text, mm.widgets.metadata_tag.text) | |
mm.run = false | |
table.insert(mm.metadata_widgets, create_new_metadata_selector(mm.widgets.metadata_name.text, mm.widgets.metadata_tag.text)) | |
mm.run = true | |
table.insert(mm.widgets.metadata_box, mm.metadata_widgets[#mm.metadata_widgets]) | |
update_button_sensitivity() | |
-- push widget into widget box | |
mm.widgets.metadata_name.text = "" | |
mm.widgets.metadata_tag.text = "" | |
end | |
} | |
mm.widgets.select_all = dt.new_widget("button"){ | |
label = "select all", | |
tooltip = "select all metadata entries", | |
clicked_callback = function(this) | |
for _, widget in ipairs(mm.metadata_widgets) do | |
widget.value = true | |
end | |
save_preferences() | |
end | |
} | |
mm.widgets.select_none = dt.new_widget("button"){ | |
label = "select none", | |
tooltip = "clear metadata selections", | |
clicked_callback = function(this) | |
for _, widget in ipairs(mm.metadata_widgets) do | |
widget.value = false | |
end | |
save_preferences() | |
end | |
} | |
mm.widgets.invert_selection = dt.new_widget("button"){ | |
label = "invert_selection", | |
tooltip = "invert selected metatdata", | |
clicked_callback = function(this) | |
for _, widget in ipairs(mm.metadata_widgets) do | |
if widget.value then | |
widget.value = false | |
else | |
widget.value = true | |
end | |
end | |
save_preferences() | |
end | |
} | |
mm.widgets.activate = dt.new_widget("button"){ | |
label = "activate", | |
tooltip = "add selected metadata to image information", | |
clicked_callback = function(this) | |
update_image_info(true) | |
end | |
} | |
mm.widgets.deactivate = dt.new_widget("button"){ | |
label = "deactivate", | |
tooltip = "remove selected metadata from image information", | |
clicked_callback = function(this) | |
update_image_info(false) | |
end | |
} | |
mm.widgets.remove = dt.new_widget("button"){ | |
label = "remove from list", | |
tooltip = "remove metadata selector from list", | |
clicked_callback = function(this) | |
local selected = {} | |
for _, md in ipairs(mm.metadata_pairs) do | |
if md.registered then | |
update_image_info(false) | |
end | |
if md.selected then | |
table.insert(selected, md.name) | |
end | |
end | |
remove_metadata_pair(selected) | |
end | |
} | |
mm.widgets.metadata_box = dt.new_widget("box"){ | |
orientation = "vertical", | |
} | |
mm.widgets.button_box = dt.new_widget("box"){ | |
orientation = "vertical", | |
dt.new_widget("section_label"){label = "add metadata selectors"}, | |
dt.new_widget("label"){label = "metadata display name"}, | |
mm.widgets.metadata_name, | |
dt.new_widget("label"){label = "exiv2 metadata tag"}, | |
mm.widgets.metadata_tag, | |
mm.widgets.create_metadata, | |
dt.new_widget("section_label"){label = "select metadata"}, | |
dt.new_widget("box"){ | |
orientation = "horizontal", | |
mm.widgets.select_all, | |
mm.widgets.select_none, | |
}, | |
mm.widgets.invert_selection, | |
dt.new_widget("section_label"){label = "selected metadata"}, | |
dt.new_widget("box"){ | |
orientation = "horizontal", | |
mm.widgets.activate, | |
mm.widgets.deactivate | |
}, | |
mm.widgets.remove | |
} | |
mm.widgets.main_box = dt.new_widget("box"){ | |
orientation = "vertical", | |
mm.widgets.metadata_box, | |
dt.new_widget("separator"){}, | |
mm.widgets.button_box | |
} | |
if dt.gui.current_view().id == "lighttable" then | |
install_module() | |
else | |
if not mm.event_registered then | |
dt.register_event( | |
"view-changed", | |
function(event, old_view, new_view) | |
if new_view.name == "lighttable" and old_view.name == "darkroom" then | |
install_module() | |
end | |
end | |
) | |
mm.event_registered = true | |
end | |
end | |
local script_data = {} | |
script_data.destroy = destroy | |
script_data.destroy_method = "hide" | |
script_data.restart = restart | |
log.log_level(log_level) | |
return script_data |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
After 494 add a line
script_data.show = restart