Merge branch 'master' into ext_editor_v3
This commit is contained in:
@@ -105,6 +105,7 @@ The following third-party projects are listed for information only. Think of thi
|
||||
* [johnnyrun/darktable_lua_gimp](https://github.com/johnnyrun/darktable_lua_gimp) – GIMP export
|
||||
* [arru/darktable-scripts](https://github.com/arru/darktable-scripts)
|
||||
* [nbremond77/darktable](https://github.com/nbremond77/darktable/tree/master/scripts)
|
||||
* [s5k6/dtscripts](https://github.com/s5k6/dtscripts)
|
||||
|
||||
## Download and Install
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ local function _(msgid)
|
||||
end
|
||||
|
||||
-- maximum number of external programs, can be increased to necessity
|
||||
local MAX_EDITORS = 10
|
||||
local MAX_EDITORS = 9
|
||||
|
||||
-- number of valid entries in the list of external programs
|
||||
local n_entries
|
||||
@@ -98,7 +98,6 @@ local n_entries
|
||||
local allowed_file_types = {"JPG", "jpg", "JPEG", "jpeg", "TIF", "tif", "TIFF", "tiff", "EXR", "exr", "PNG", "png"}
|
||||
|
||||
|
||||
-- last used editor initialization
|
||||
if not dt.preferences.read(MODULE_NAME, "initialized", "bool") then
|
||||
dt.preferences.write(MODULE_NAME, "lastchoice", "integer", 0)
|
||||
dt.preferences.write(MODULE_NAME, "initialized", "bool", true)
|
||||
|
||||
@@ -69,14 +69,8 @@ end
|
||||
-- preferences
|
||||
|
||||
if not dt.preferences.read(MODULE, "initialized", "bool") then
|
||||
dt.preferences.write(MODULE, "unknown_tag", "string", "unknown_person")
|
||||
dt.preferences.write(MODULE, "ignore_tags", "string", "")
|
||||
dt.preferences.write(MODULE, "tolerance", "float", 0.6)
|
||||
dt.preferences.write(MODULE, "num_cores", "integer", 0)
|
||||
dt.preferences.write(MODULE, "known_image_path", "directory", dt.configuration.config_dir .. "/face_recognition")
|
||||
dt.preferences.write(MODULE, "export_format", "integer", 1)
|
||||
dt.preferences.write(MODULE, "max_width", "integer", 1000)
|
||||
dt.preferences.write(MODULE, "max_height", "integer", 1000)
|
||||
reset_preferences()
|
||||
save_preferences()
|
||||
dt.preferences.write(MODULE, "initialized", "bool", true)
|
||||
end
|
||||
|
||||
@@ -154,13 +148,29 @@ end
|
||||
|
||||
local function save_preferences()
|
||||
dt.preferences.write(MODULE, "unknown_tag", "string", fc.unknown_tag.text)
|
||||
dt.preferences.write(MODULE, "no_persons_found_tag", "string", fc.no_persons_found_tag.text)
|
||||
dt.preferences.write(MODULE, "ignore_tags", "string", fc.ignore_tags.text)
|
||||
dt.preferences.write(MODULE, "max_width", "integer", tonumber(fc.width.text))
|
||||
dt.preferences.write(MODULE, "max_height", "integer", tonumber(fc.height.text))
|
||||
dt.preferences.write(MODULE, "num_cores", "integer", fc.num_cores.value)
|
||||
dt.preferences.write(MODULE, "known_image_path", "directory", fc.known_image_path.value)
|
||||
local val = fc.tolerance.value
|
||||
val = string.gsub(tostring(val), ",", ".")
|
||||
dt.preferences.write(MODULE, "tolerance", "float", tonumber(val))
|
||||
dt.preferences.write(MODULE, "num_cores", "integer", fc.num_cores.value)
|
||||
dt.preferences.write(MODULE, "export_format", "integer", fc.export_format.selected)
|
||||
dt.preferences.write(MODULE, "max_width", "integer", tonumber(fc.width.text))
|
||||
dt.preferences.write(MODULE, "max_height", "integer", tonumber(fc.height.text))
|
||||
end
|
||||
|
||||
local function reset_preferences()
|
||||
fc.unknown_tag.text = "unknown_person"
|
||||
fc.no_persons_found_tag.text = "no_persons_found"
|
||||
fc.ignore_tags.text = ""
|
||||
fc.known_image_path.value = dt.configuration.config_dir .. "/face_recognition"
|
||||
fc.tolerance.value = 0.6
|
||||
fc.num_cores.value = -1
|
||||
fc.export_format.selected = 1
|
||||
fc.width.text = 1000
|
||||
fc.height.text = 1000
|
||||
save_preferences()
|
||||
end
|
||||
|
||||
-- Check if image has ignored tag attached
|
||||
@@ -205,6 +215,7 @@ local function face_recognition ()
|
||||
local nrCores = dt.preferences.read(MODULE, "num_cores", "integer")
|
||||
local ignoreTagString = dt.preferences.read(MODULE, "ignore_tags", "string")
|
||||
local unknownTag = dt.preferences.read(MODULE, "unknown_tag", "string")
|
||||
local nonpersonsfoundTag = dt.preferences.read(MODULE, "no_persons_found_tag", "string")
|
||||
|
||||
-- face_recognition uses -1 for all cores, we use 0 in preferences
|
||||
if nrCores < 1 then
|
||||
@@ -259,13 +270,15 @@ local function face_recognition ()
|
||||
dt.print(_("processing results..."))
|
||||
local result = {}
|
||||
for line in io.lines(OUTPUT) do
|
||||
local file, tag = string.match (line, "(.*),(.*)$")
|
||||
tag = string.gsub (tag, "%d*$", "")
|
||||
dt.print_log ("File:"..file .." Tag:".. tag)
|
||||
if result[file] ~= nil then
|
||||
table.insert (result[file], tag)
|
||||
else
|
||||
result[file] = {tag}
|
||||
if not string.match(line, "^WARNING:") then
|
||||
local file, tag = string.match (line, "(.*),(.*)$")
|
||||
tag = string.gsub (tag, "%d*$", "")
|
||||
dt.print_log ("File:"..file .." Tag:".. tag)
|
||||
if result[file] ~= nil then
|
||||
table.insert (result[file], tag)
|
||||
else
|
||||
result[file] = {tag}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -283,10 +296,16 @@ local function face_recognition ()
|
||||
if t == "unknown_person" then
|
||||
t = unknownTag
|
||||
end
|
||||
dt.print_log ("ImgId:" .. img.id .. " Tag:".. t)
|
||||
-- Create tag if it does not exists
|
||||
local tag = dt.tags.create (t)
|
||||
img:attach_tag (tag)
|
||||
-- Check of unrecognized no_persons_found
|
||||
if t == "no_persons_found" then
|
||||
t = nonpersonsfoundTag
|
||||
end
|
||||
if t ~= "" and t ~= nil then
|
||||
dt.print_log ("ImgId:" .. img.id .. " Tag:".. t)
|
||||
-- Create tag if it does not exists
|
||||
local tag = dt.tags.create (t)
|
||||
img:attach_tag (tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -314,6 +333,12 @@ fc.unknown_tag = dt.new_widget("entry"){
|
||||
editable = true,
|
||||
}
|
||||
|
||||
fc.no_persons_found_tag = dt.new_widget("entry"){
|
||||
text = dt.preferences.read(MODULE, "no_persons_found_tag", "string"),
|
||||
tooltip = _("tag to be used when no persons are found"),
|
||||
editable = true,
|
||||
}
|
||||
|
||||
fc.ignore_tags = dt.new_widget("entry"){
|
||||
text = dt.preferences.read(MODULE, "ignore_tags", "string"),
|
||||
tooltip = _("tags of images to ignore"),
|
||||
@@ -386,7 +411,9 @@ fc.execute = dt.new_widget("button"){
|
||||
local widgets = {
|
||||
dt.new_widget("label"){ label = _("unknown person tag")},
|
||||
fc.unknown_tag,
|
||||
dt.new_widget("label"){ label = _("togs of images to ignore")},
|
||||
dt.new_widget("label"){ label = _("no persons found tag")},
|
||||
fc.no_persons_found_tag,
|
||||
dt.new_widget("label"){ label = _("tags of images to ignore")},
|
||||
fc.ignore_tags,
|
||||
dt.new_widget("label"){ label = _("face data directory")},
|
||||
fc.known_image_path,
|
||||
@@ -413,7 +440,10 @@ table.insert(widgets, fc.execute)
|
||||
|
||||
fc.widget = dt.new_widget("box"){
|
||||
orientation = vertical,
|
||||
table.unpack(widgets)
|
||||
reset_callback = function(this)
|
||||
reset_preferences()
|
||||
end,
|
||||
table.unpack(widgets),
|
||||
}
|
||||
|
||||
--fc.tolerance.value = dt.preferences.read(MODULE, "tolerance", "float")
|
||||
@@ -425,7 +455,7 @@ dt.register_lib(
|
||||
"face_recognition", -- Module name
|
||||
_("face recognition"), -- Visible name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 300}}, -- containers
|
||||
fc.widget,
|
||||
nil,-- view_enter
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2014 Jérémy Rosen
|
||||
copyright (c) 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 <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
INCLUDE ALL
|
||||
Automatically include all scripts in the script repository
|
||||
|
||||
This is intended for debugging purpose
|
||||
|
||||
|
||||
USAGE
|
||||
* require this file from your main lua config file:
|
||||
* go to configuration => preferences
|
||||
* Enable the scripts you want to use
|
||||
* restart darktable
|
||||
|
||||
Note that you need to restart DT for your changes to enabled scripts to take effect
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
local io = require "io"
|
||||
|
||||
-- must be loaded for scripts using darktable.control_execute to work
|
||||
require "official/yield"
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- find all scripts, but skip the lib and tools directories
|
||||
local output = io.popen("cd "..dt.configuration.config_dir.."/lua ;find . -name lib -prune -o -name tools -prune -o -name \\*.lua -print")
|
||||
|
||||
local my_name={...}
|
||||
my_name = my_name[1]
|
||||
for line in output:lines() do
|
||||
local req_name = line:sub(3,-5)
|
||||
if req_name ~= my_name and not string.match(req_name, "yield") then
|
||||
dt.preferences.register(my_name,req_name,"bool","enable "..req_name,
|
||||
"Should the script "..req_name.." be enabled at next startup",false)
|
||||
|
||||
if dt.preferences.read(my_name,req_name,"bool") then
|
||||
require(req_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
@@ -174,6 +174,59 @@ function dtutils_string.urlencode(str)
|
||||
end
|
||||
|
||||
|
||||
dtutils_string.libdoc.functions["is_not_sanitized"] = {
|
||||
Name = [[is_not_sanitized]],
|
||||
Synopsis = [[Check if a string has been sanitized]],
|
||||
Usage = [[local ds = require "lib/dtutils.string"
|
||||
local result = ds.is_not_sanitized(str)
|
||||
str - string - the string that needs to be made safe]],
|
||||
Description = [[is_not_sanitized checks a string to see if it
|
||||
has been made safe use passing as an argument in a system command.]],
|
||||
Return_Value = [[result - boolean - true if the string is not sanitized otherwise false]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
local function _is_not_sanitized_posix(str)
|
||||
-- A sanitized string must be quoted.
|
||||
if not string.match(str, "^'.*'$") then
|
||||
return true
|
||||
-- A quoted string containing no quote characters within is sanitized.
|
||||
elseif string.match(str, "^'[^']*'$") then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Any quote characters within a sanitized string must be properly
|
||||
-- escaped.
|
||||
local quotesStripped = string.sub(str, 2, -2)
|
||||
local escapedQuotesRemoved = string.gsub(quotesStripped, "'\\''", "")
|
||||
if string.find(escapedQuotesRemoved, "'") then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function _is_not_sanitized_windows(str)
|
||||
if not string.match(str, "^\".*\"$") then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function dtutils_string.is_not_sanitized(str)
|
||||
if dt.configuration.running_os == "windows" then
|
||||
return _is_not_sanitized_windows(str)
|
||||
else
|
||||
return _is_not_sanitized_posix(str)
|
||||
end
|
||||
end
|
||||
|
||||
dtutils_string.libdoc.functions["sanitize"] = {
|
||||
Name = [[sanitize]],
|
||||
Synopsis = [[surround a string in quotes making it safe to pass as an argument]],
|
||||
@@ -192,41 +245,27 @@ dtutils_string.libdoc.functions["sanitize"] = {
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_string.sanitize(str)
|
||||
local result = ""
|
||||
local os_quote = dt.configuration.running_os == "windows" and '"' or "'"
|
||||
|
||||
if dtutils_string.is_not_sanitized(str) then
|
||||
result = os_quote .. str .. os_quote
|
||||
local function _sanitize_posix(str)
|
||||
if _is_not_sanitized_posix(str) then
|
||||
return "'" .. string.gsub(str, "'", "'\\''") .. "'"
|
||||
else
|
||||
return str
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
dtutils_string.libdoc.functions["is_not_sanitized"] = {
|
||||
Name = [[is_not_sanitized]],
|
||||
Synopsis = [[Check if a string has been sanitized]],
|
||||
Usage = [[local ds = require "lib/dtutils.string"
|
||||
local result = ds.is_not_sanitized(str)
|
||||
str - string - the string that needs to be made safe]],
|
||||
Description = [[is_not_sanitized checks a string to see if it
|
||||
has been made safe use passing as an argument in a system command.]],
|
||||
Return_Value = [[result - boolean - true if the string is not sanitized otherwise false]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_string.is_not_sanitized(str)
|
||||
local os_quote = dt.configuration.running_os == "windows" and '"' or "'"
|
||||
|
||||
if string.match(str, os_quote .. ".*" .. os_quote) then
|
||||
return false
|
||||
local function _sanitize_windows(str)
|
||||
if _is_not_sanitized_windows(str) then
|
||||
return "\"" .. string.gsub(str, "\"", "\"^\"\"") .. "\""
|
||||
else
|
||||
return true
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
function dtutils_string.sanitize(str)
|
||||
if dt.configuration.running_os == "windows" then
|
||||
return _sanitize_windows(str)
|
||||
else
|
||||
return _sanitize_posix(str)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user