Compare commits
284 Commits
release-2.
...
API-4.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97dd8a1257 | ||
|
|
83fc339c4b | ||
|
|
693c535428 | ||
|
|
d74f2eba92 | ||
|
|
d0ec941009 | ||
|
|
178528508b | ||
|
|
20a31b39e0 | ||
|
|
fc252d5e7e | ||
|
|
4090266989 | ||
|
|
19b7d29483 | ||
|
|
af4e3884bb | ||
|
|
d3742bb410 | ||
|
|
75cb1a5fdd | ||
|
|
8436c878e8 | ||
|
|
63d3a34f46 | ||
|
|
6cda97c335 | ||
|
|
a40d8b8bc5 | ||
|
|
15a492cfa3 | ||
|
|
445e882579 | ||
|
|
d4b155d0c2 | ||
|
|
65f4960302 | ||
|
|
dc9e8d406d | ||
|
|
4d76006c2d | ||
|
|
731a6d0410 | ||
|
|
e4542aa451 | ||
|
|
ecc4a29be4 | ||
|
|
0f0b3d6df5 | ||
|
|
a826c1725b | ||
|
|
4e3b461fd3 | ||
|
|
d8a9dfafb2 | ||
|
|
ede621bea7 | ||
|
|
2f7da8356f | ||
|
|
d555cb2c94 | ||
|
|
750d70bfff | ||
|
|
dcc205ec4f | ||
|
|
356756286e | ||
|
|
79511d9b42 | ||
|
|
bff2df018b | ||
|
|
2562a218a0 | ||
|
|
d46b7e4aaf | ||
|
|
dee7ee1dde | ||
|
|
01fc339da5 | ||
|
|
ef46cd9b45 | ||
|
|
ac3ccb287b | ||
|
|
f52292e8d1 | ||
|
|
693c9858c2 | ||
|
|
c8d5e9d9d2 | ||
|
|
50668817db | ||
|
|
91758b61c3 | ||
|
|
39ae92a0fa | ||
|
|
95c203793c | ||
|
|
c7ac974ab9 | ||
|
|
51931d2b01 | ||
|
|
d271f6d46d | ||
|
|
efcd48f1d9 | ||
|
|
76daa583b3 | ||
|
|
3425a85fd0 | ||
|
|
9a6c1510fd | ||
|
|
44c391cb5a | ||
|
|
6085be7589 | ||
|
|
7d9bf8c729 | ||
|
|
1d72457dc1 | ||
|
|
0e32d2b1b4 | ||
|
|
75165e8838 | ||
|
|
d9536f1294 | ||
|
|
44e4c3ddc5 | ||
|
|
f67755f3b5 | ||
|
|
68d768cbd3 | ||
|
|
0be268c01f | ||
|
|
1831db530d | ||
|
|
8a32bf21d0 | ||
|
|
81c5bb4d52 | ||
|
|
93796bf4a8 | ||
|
|
4c34113f78 | ||
|
|
e4b189a06e | ||
|
|
0ebc885eb1 | ||
|
|
b4658ddf76 | ||
|
|
b38e2964ca | ||
|
|
5021e512b9 | ||
|
|
6d927540c8 | ||
|
|
6168767766 | ||
|
|
5af200a29d | ||
|
|
8b333c1ab5 | ||
|
|
d1f34ec136 | ||
|
|
c887a0d2b5 | ||
|
|
1168ba4557 | ||
|
|
e389420f07 | ||
|
|
0590beb876 | ||
|
|
d30c0d0213 | ||
|
|
eb774d37c6 | ||
|
|
cade6d5e0b | ||
|
|
19b71c5782 | ||
|
|
5f802a8179 | ||
|
|
99e1141446 | ||
|
|
3bd76b4fb5 | ||
|
|
3fb4ef34f4 | ||
|
|
f7c2abdcd7 | ||
|
|
cc6006e226 | ||
|
|
2816c2bcf5 | ||
|
|
abc8061cd7 | ||
|
|
a30c4d6be5 | ||
|
|
0bd5b6ba5d | ||
|
|
4ea3a500e8 | ||
|
|
56925c1eed | ||
|
|
71e7269927 | ||
|
|
6babff1be1 | ||
|
|
fec000086f | ||
|
|
bddf4cedb4 | ||
|
|
91c62e208b | ||
|
|
1e0424a83f | ||
|
|
c74d29e020 | ||
|
|
eadfff9cd0 | ||
|
|
93e30a8542 | ||
|
|
85ccc663c8 | ||
|
|
dab02823ff | ||
|
|
4fccfbf51d | ||
|
|
b04e322df8 | ||
|
|
f550822943 | ||
|
|
564fd20f23 | ||
|
|
45e38a9a2d | ||
|
|
e51f1e0a82 | ||
|
|
b4b82906ea | ||
|
|
a254ac87f7 | ||
|
|
ae8727ad96 | ||
|
|
de823f0430 | ||
|
|
c6ced0b1ea | ||
|
|
e76a883053 | ||
|
|
f967e96aa5 | ||
|
|
c8713a7f38 | ||
|
|
73e274cf0b | ||
|
|
c75c3d0734 | ||
|
|
e74b253f64 | ||
|
|
45009aedff | ||
|
|
870ba65662 | ||
|
|
80ace33af1 | ||
|
|
f69455bdb7 | ||
|
|
4ed6dc276e | ||
|
|
3dd415697a | ||
|
|
f7ce368b28 | ||
|
|
168236780d | ||
|
|
549e9ccd0b | ||
|
|
21f31b403b | ||
|
|
3b42503bd3 | ||
|
|
95e15fc40c | ||
|
|
74e546c2ce | ||
|
|
0c0f97560e | ||
|
|
8392f6f5a6 | ||
|
|
9a5ede0e6d | ||
|
|
33417454ed | ||
|
|
d79237958f | ||
|
|
6ef06467b6 | ||
|
|
a85d2222eb | ||
|
|
2325d24d6f | ||
|
|
4397e07d7f | ||
|
|
f5a624cfb2 | ||
|
|
d2762fae73 | ||
|
|
22d7107305 | ||
|
|
e22df23bc9 | ||
|
|
200ef9a45e | ||
|
|
783194e253 | ||
|
|
204868d733 | ||
|
|
f61a710e9b | ||
|
|
457731700c | ||
|
|
3dcb08cca3 | ||
|
|
e3f1ff7d70 | ||
|
|
623b506691 | ||
|
|
6e5dbc0b83 | ||
|
|
9c40a22c50 | ||
|
|
9dbd1d9bda | ||
|
|
9693b017c4 | ||
|
|
670915ab36 | ||
|
|
8224913b01 | ||
|
|
2b577f9755 | ||
|
|
4556c9a055 | ||
|
|
679ce8b6ab | ||
|
|
59eeb60594 | ||
|
|
6c4f9b2c03 | ||
|
|
f6e2037a1b | ||
|
|
0c9d4a5de4 | ||
|
|
5006c9d6ea | ||
|
|
17a8c4f3d5 | ||
|
|
98cb1c2315 | ||
|
|
6cc5aa08e3 | ||
|
|
ebda3865dc | ||
|
|
fb171a459e | ||
|
|
027a249837 | ||
|
|
2a2bb7199e | ||
|
|
f64892d010 | ||
|
|
d023d7f3e4 | ||
|
|
ef96bfa98c | ||
|
|
b2ee46804b | ||
|
|
7c9c277cf8 | ||
|
|
3e376ebc74 | ||
|
|
102ea2da7a | ||
|
|
e1a2fbfaa6 | ||
|
|
c1aecf2b37 | ||
|
|
b2754e3036 | ||
|
|
abbb740753 | ||
|
|
7504224c2f | ||
|
|
57b990409a | ||
|
|
cff5e06d62 | ||
|
|
a543b409cb | ||
|
|
e57d2f4afa | ||
|
|
9da0173e05 | ||
|
|
a21b171d96 | ||
|
|
2569e7ed8e | ||
|
|
25d2eade8c | ||
|
|
43b949966d | ||
|
|
08205b96cd | ||
|
|
e2e63bbfc8 | ||
|
|
24d1a7dd67 | ||
|
|
9b4e46b625 | ||
|
|
e47a335dec | ||
|
|
d46f7023d5 | ||
|
|
19bef60752 | ||
|
|
5ff73657e1 | ||
|
|
fb8da04a18 | ||
|
|
3ee50a290f | ||
|
|
71ea05598f | ||
|
|
96d5c71e0d | ||
|
|
07318eb7aa | ||
|
|
2ef235dddd | ||
|
|
f7cc9865cc | ||
|
|
c323ce6cbb | ||
|
|
5d2ddc490a | ||
|
|
40463290a6 | ||
|
|
6b4faac2cb | ||
|
|
077a7de605 | ||
|
|
180bc06c06 | ||
|
|
72a97ecbd6 | ||
|
|
87a6ed3e46 | ||
|
|
95d8e777db | ||
|
|
d028ecfc55 | ||
|
|
225da821e4 | ||
|
|
7cd4023f5c | ||
|
|
2379832cc2 | ||
|
|
cb46e090dd | ||
|
|
a656f0f304 | ||
|
|
564fa313f1 | ||
|
|
47cd618bf4 | ||
|
|
ae709a1d9b | ||
|
|
0812d96068 | ||
|
|
86911933db | ||
|
|
69d5c139f6 | ||
|
|
405b9a19f9 | ||
|
|
c4a696cbc4 | ||
|
|
bb0e4997de | ||
|
|
bc1b908063 | ||
|
|
29c522484c | ||
|
|
5b6585b115 | ||
|
|
3715c69c02 | ||
|
|
3d3b457c92 | ||
|
|
e4f5d44658 | ||
|
|
fc7a25ca4d | ||
|
|
1d3a44a157 | ||
|
|
319b44f153 | ||
|
|
98a4144403 | ||
|
|
a819140c88 | ||
|
|
cba5c65608 | ||
|
|
eff4a3abcb | ||
|
|
2e0ccef3f5 | ||
|
|
429a157715 | ||
|
|
76065ee93f | ||
|
|
97def2240f | ||
|
|
15cd18a5c4 | ||
|
|
0b3b095eac | ||
|
|
38bda12c4b | ||
|
|
d2a34226a6 | ||
|
|
92dcb29010 | ||
|
|
d4cc869227 | ||
|
|
291ff86686 | ||
|
|
895ad62757 | ||
|
|
4acfa36b57 | ||
|
|
fe18256c25 | ||
|
|
39531eb4f0 | ||
|
|
28d73b44a6 | ||
|
|
3983a8a1ba | ||
|
|
08151e7e21 | ||
|
|
446927f947 | ||
|
|
5d72904849 | ||
|
|
ff1f076536 | ||
|
|
c56766ef85 | ||
|
|
eff8953207 | ||
|
|
2ba1ae7a23 |
@@ -1,9 +1,9 @@
|
||||
lua-scripts
|
||||
===========
|
||||
|
||||
The Lua scripts in this repository are meant to be used together with darktable. Either copy them individually to `~/.config/darktable/lua` (you might have to create that folder) or just copy/symlink the whole repository there. That allows to update all your scripts with a simple call to `git pull`.
|
||||
The Lua scripts in this repository are meant to be used together with darktable. Either copy them individually to `~/.config/darktable/lua` (Linux/Unix) or `%LOCALAPPDATA%\darktable\lua` (Windows) (you might have to create that folder) or just copy/symlink the whole repository there. That allows to update all your scripts with a simple call to `git pull`.
|
||||
|
||||
To enable one of the scripts you have to add a line like `require "official/hello_world"` to your `~/.config/darktable/luarc` file which will enable the example script in `official/hello_world.lua` (note the lack of the `.lua` suffix).
|
||||
To enable one of the scripts you have to add a line like `require "examples/hello_world"` to your: `~/.config/darktable/luarc` (Linux/Unix) or `%LOCALAPPDATA%\darktable\luarc` (Windows) file which will enable the example script in `examples/hello_world.lua` (note the lack of the `.lua` suffix).
|
||||
|
||||
Each script includes its own documentation and usage in its header, please refer to them.
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ USAGE
|
||||
> exiftool -AutoDynamicRange XE021351.RAF
|
||||
Auto Dynamic Range : 200%
|
||||
|
||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* exiftool
|
||||
|
||||
LICENSE
|
||||
GPLv2
|
||||
@@ -41,7 +43,7 @@ local darktable = require "darktable"
|
||||
local autostyle_apply_one_image,autostyle_apply_one_image_event,autostyle_apply,exiftool_attribute,capture
|
||||
|
||||
-- Tested it with darktable 1.6.1 and darktable git from 2014-01-25
|
||||
darktable.configuration.check_version(...,{2,0,2},{2,1,0})
|
||||
darktable.configuration.check_version(...,{2,0,2},{2,1,0},{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Receive the event triggered
|
||||
function autostyle_apply_one_image_event(event,image)
|
||||
@@ -66,7 +68,11 @@ function autostyle_apply_one_image (image)
|
||||
darktable.print("style name not found in " .. darktable.preferences.read("autostyle","exif_tag","string"))
|
||||
return
|
||||
end
|
||||
|
||||
if not checkIfBinExists("exiftool") then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- First find the style (we have its name)
|
||||
local styles= darktable.styles
|
||||
local style
|
||||
@@ -118,8 +124,7 @@ end
|
||||
function get_stdout(cmd)
|
||||
-- Open the command, for reading
|
||||
local fd = assert(io.popen(cmd, 'r'))
|
||||
-- yield to other lua threads until data is ready to be read
|
||||
coroutine.yield("FILE_READABLE",fd)
|
||||
dt.control.read(fd)
|
||||
-- slurp the whole file
|
||||
local data = assert(fd:read('*a'))
|
||||
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright 2014 by Tobias Jakobs.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
darktable calc distance script
|
||||
|
||||
This script calcs the distance between two images using the GPS data in the metadata
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
* register a shortcut
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,1})
|
||||
|
||||
local function calcDistance()
|
||||
local sel_images = dt.gui.selection()
|
||||
|
||||
local lat1 = 0;
|
||||
local lon1 = 0;
|
||||
local lat2 = 0;
|
||||
local lon2 = 0;
|
||||
local i = 0;
|
||||
|
||||
local sel_images = dt.gui.selection()
|
||||
|
||||
for _,image in ipairs(sel_images) do
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
|
||||
i = i + 1;
|
||||
|
||||
if (i == 1) then
|
||||
lat1 = image.latitude;
|
||||
lon1 = image.longitude;
|
||||
end
|
||||
|
||||
lat2 = image.latitude;
|
||||
lon2 = image.longitude;
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- I used code from here:
|
||||
-- http://stackoverflow.com/questions/27928/how-do-i-calculate-distance-between-two-latitude-longitude-points
|
||||
|
||||
local earthRadius = 6371; -- Radius of the earth in km
|
||||
local dLat = math.rad(lat2-lat1); -- deg2rad below
|
||||
local dLon = math.rad(lon2-lon1);
|
||||
local a =
|
||||
math.sin(dLat/2) * math.sin(dLat/2) +
|
||||
math.cos(math.rad(lat1)) * math.cos(math.rad(lat2)) *
|
||||
math.sin(dLon/2) * math.sin(dLon/2)
|
||||
;
|
||||
local angle = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a));
|
||||
local distance = earthRadius * angle; -- Distance in km
|
||||
|
||||
dt.print(distance.." km")
|
||||
|
||||
|
||||
end
|
||||
|
||||
-- Register
|
||||
dt.register_event("shortcut",calcDistance,"Calculate the distance from latitude and longitude in km")
|
||||
75
contrib/clear_GPS.lua
Normal file
75
contrib/clear_GPS.lua
Normal file
@@ -0,0 +1,75 @@
|
||||
--[[
|
||||
|
||||
clear_GPS.lua - export and edit with GIMP
|
||||
|
||||
Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
clear_GPS - clear GPS data from selected image(s)
|
||||
|
||||
This shortcut removes the GPS coordinate data from the selected images.
|
||||
|
||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* None
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
* select an image or images
|
||||
* click the shortcut, clear GPS data
|
||||
|
||||
BUGS, COMMENTS, SUGGESTIONS
|
||||
* Send to Bill Ferguson, wpferguson@gmail.com
|
||||
|
||||
CHANGES
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local gettext = dt.gettext
|
||||
|
||||
-- not a number
|
||||
local NaN = 0/0
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0})
|
||||
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("clear_GPS",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("clear_GPS", msgid)
|
||||
end
|
||||
|
||||
local function clear_GPS(images)
|
||||
for _, image in ipairs(images) do
|
||||
-- set the location information to Not a Number (NaN) so it displays correctly
|
||||
image.elevation = NaN
|
||||
image.latitude = NaN
|
||||
image.longitude = NaN
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
dt.gui.libs.image.register_action(
|
||||
_("clear GPS data"),
|
||||
function(event, images) clear_GPS(images) end,
|
||||
"clear GPS data"
|
||||
)
|
||||
|
||||
dt.register_event(
|
||||
"shortcut",
|
||||
function(event, shortcut) clear_GPS(dt.gui.action_images) end,
|
||||
"clear GPS data"
|
||||
)
|
||||
@@ -1,6 +1,7 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright 2014-2015 by Christian Kanzian.
|
||||
Copyright 2014-2016 by Christian Kanzian
|
||||
Copyright 2016 by Holger Klemm
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -17,8 +18,9 @@
|
||||
]]
|
||||
|
||||
--[[
|
||||
(MULTI-)COPY ATTACH DETACH TAG
|
||||
A simple script that will create three shortcuts to copy, attach and detach image tags.
|
||||
(MULTI-)COPY ATTACH DETACH REPLACE TAGS
|
||||
This script that will create four shortcuts and add a modul in lighttable mode to copy,
|
||||
paste, replace and remove tags from images.
|
||||
|
||||
INSTALATION
|
||||
* copy this file in $CONFIGDIR/lua/ where CONFIGDIR is your darktable configuration directory
|
||||
@@ -27,17 +29,33 @@ INSTALATION
|
||||
USAGE
|
||||
* set the shortcuts for copy, attach and detach in the preferences dialog
|
||||
* <your shortcut1> copy will create a list of tags from all selected images
|
||||
* <your shortcut2> attaches copied tags to selected images, whereby
|
||||
* <your shortcut2> paste copied tags to selected images, whereby
|
||||
darktable internal tags starting with 'darktable|' will not be touched
|
||||
* <your shortcut3> detach removes all expect darktable internal tags from selected images
|
||||
|
||||
* <your shortcut3> removes all expect darktable internal tags from selected images
|
||||
* <your shortcut4> replaces all tags expect darktable internals
|
||||
* A module reset will empty the clipboard
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,0})
|
||||
local debug = require "darktable.debug"
|
||||
|
||||
local gettext = dt.gettext
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("copy_attach_detach_tags",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("copy_attach_detach_tags", msgid)
|
||||
end
|
||||
|
||||
|
||||
local image_tags = {}
|
||||
|
||||
|
||||
local taglist_label = dt.new_widget("label"){selectable = true, ellipsize = "middle", halign = "start"}
|
||||
taglist_label.label = ""
|
||||
|
||||
local function mcopy_tags()
|
||||
local sel_images = dt.gui.action_images
|
||||
local tag_list_tmp = {}
|
||||
@@ -56,7 +74,7 @@ local function mcopy_tags()
|
||||
|
||||
--remove duplicate and 'darktable|' tags create final image_tags
|
||||
for _,k in ipairs(tag_list_tmp) do
|
||||
|
||||
|
||||
if not string.match(tostring(k), 'darktable|') then
|
||||
if not hash[k] then
|
||||
image_tags[#image_tags+1] = k
|
||||
@@ -65,23 +83,38 @@ local function mcopy_tags()
|
||||
end
|
||||
end
|
||||
|
||||
dt.print("Image tags copied ...")
|
||||
return(image_tags)
|
||||
dt.print(_('Image tags copied ...'))
|
||||
|
||||
--create UI tag list
|
||||
local taglist = ""
|
||||
|
||||
for _,tag in ipairs(image_tags) do
|
||||
if taglist == "" then
|
||||
taglist = tostring(tag)
|
||||
else
|
||||
taglist = taglist.."\n"..tostring(tag)
|
||||
end
|
||||
end
|
||||
|
||||
taglist_label.label = taglist
|
||||
|
||||
return(image_tags)
|
||||
end
|
||||
|
||||
|
||||
-- attach copied tags to all selected images
|
||||
local function attach_tags()
|
||||
|
||||
|
||||
if next(image_tags) == nil then
|
||||
dt.print("No tags to attached, please copy tags first.")
|
||||
dt.print(_('No tags to attached, please copy tags first.'))
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local sel_images = dt.gui.action_images
|
||||
|
||||
for _,image in ipairs(sel_images) do
|
||||
local present_image_tags = {}
|
||||
present_image_tags = dt.tags.get_tags(image)
|
||||
|
||||
|
||||
for _,ct in ipairs(image_tags) do
|
||||
-- check if image has tags and attach
|
||||
if next(present_image_tags) == nil then
|
||||
@@ -96,43 +129,111 @@ local function attach_tags()
|
||||
end
|
||||
end
|
||||
end
|
||||
dt.print("Tags attached ...")
|
||||
dt.print(_('Tags attached ...'))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
local function detach_tags()
|
||||
local sel_images = dt.gui.action_images
|
||||
|
||||
for _,image in ipairs(sel_images) do
|
||||
local present_image_tags = {}
|
||||
present_image_tags = dt.tags.get_tags(image)
|
||||
|
||||
|
||||
for _,present_tag in ipairs(present_image_tags) do
|
||||
if not string.match(tostring(present_tag), 'darktable|') then
|
||||
dt.tags.detach(present_tag,image)
|
||||
end
|
||||
end
|
||||
end
|
||||
dt.print("Tags removed from image(s).")
|
||||
dt.print(_('Tags removed from image(s).'))
|
||||
end
|
||||
|
||||
local function replace_tags()
|
||||
detach_tags()
|
||||
attach_tags()
|
||||
dt.print(_('Tags replaced'))
|
||||
end
|
||||
|
||||
-- create modul Tagging addons
|
||||
taglist_label.reset_callback = mcopy_tags
|
||||
|
||||
-- create buttons and elements
|
||||
local taglabel = dt.new_widget("label") {
|
||||
label = _('tag clipboard'),
|
||||
selectable = false,
|
||||
ellipsize = "middle",
|
||||
halign = "start"}
|
||||
|
||||
local box1 = dt.new_widget("box"){
|
||||
orientation = "horizontal",
|
||||
dt.new_widget("button") {
|
||||
label = _('multi copy tags'),
|
||||
clicked_callback = mcopy_tags},
|
||||
dt.new_widget("button") {
|
||||
label = _('paste tags'),
|
||||
clicked_callback = attach_tags}
|
||||
|
||||
}
|
||||
|
||||
local box2 = dt.new_widget("box"){
|
||||
orientation = "horizontal",
|
||||
dt.new_widget("button") {
|
||||
label = _('replace tags'),
|
||||
clicked_callback = replace_tags},
|
||||
dt.new_widget("button") {
|
||||
label = _('remove all tags'),
|
||||
clicked_callback = detach_tags}
|
||||
}
|
||||
|
||||
local sep = dt.new_widget("separator"){}
|
||||
|
||||
-- pack elements into widget table for a nicer layout
|
||||
local widget_table = {}
|
||||
|
||||
widget_table[1] = box1
|
||||
widget_table[#widget_table+1] = box2
|
||||
|
||||
widget_table[#widget_table+1] = sep
|
||||
widget_table[#widget_table+1] = taglabel
|
||||
widget_table[#widget_table+1] = taglist_label
|
||||
|
||||
|
||||
-- create modul
|
||||
dt.register_lib("tagging_addon","Tagging addon",true,true,{
|
||||
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",500}
|
||||
},
|
||||
dt.new_widget("box") {
|
||||
-- orientation = "vertical",
|
||||
reset_callback = function()
|
||||
taglist_label.label = ""
|
||||
image_tags = {}
|
||||
end,
|
||||
table.unpack(widget_table),
|
||||
},
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
|
||||
-- shortcut for copy
|
||||
dt.register_event("shortcut",
|
||||
mcopy_tags,
|
||||
"copy tags from selected image(s)")
|
||||
_('copy tags from selected image(s)'))
|
||||
|
||||
-- shortcut for attach
|
||||
dt.register_event("shortcut",
|
||||
attach_tags,
|
||||
"attach tags to selected image(s)")
|
||||
_('paste tags to selected image(s)'))
|
||||
|
||||
-- shortcut for detaching tags
|
||||
dt.register_event("shortcut",
|
||||
detach_tags,
|
||||
"detach tags to selected image(s)")
|
||||
_('remove tags from selected image(s)'))
|
||||
|
||||
-- shortcut for replace tags
|
||||
dt.register_event("shortcut",
|
||||
replace_tags,
|
||||
_('replace tags from selected image(s)'))
|
||||
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;
|
||||
|
||||
@@ -34,8 +34,8 @@ USAGE
|
||||
|
||||
local darktable = require "darktable"
|
||||
|
||||
-- Tested with darktable 1.6.2
|
||||
darktable.configuration.check_version(...,{2,0,2})
|
||||
-- Tested with darktable 2.0.1
|
||||
darktable.configuration.check_version(...,{2,0,0},{3,0,0},{4,0,0})
|
||||
|
||||
local queue = {}
|
||||
local processed_files = {}
|
||||
@@ -60,7 +60,7 @@ end
|
||||
local function convert_image(image)
|
||||
if string.sub(image.filename, -3) == "CR2" then
|
||||
local filename = image.path .. "/" .. image.filename
|
||||
local result = coroutine.yield("RUN_COMMAND", "cr2hdr " .. filename)
|
||||
local result = dt.control.execute( "cr2hdr " .. filename)
|
||||
local out_filename = string.gsub(filename, ".CR2", ".DNG")
|
||||
local file = io.open(out_filename)
|
||||
if file then
|
||||
|
||||
70
contrib/fujifilm_ratings.lua
Normal file
70
contrib/fujifilm_ratings.lua
Normal file
@@ -0,0 +1,70 @@
|
||||
--[[ fujifilm_ratings-0.1
|
||||
|
||||
Support for importing Fujifilm in-camera ratings in darktable.
|
||||
|
||||
Copyright (C) 2017 Ben Mendis <ben.mendis@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Dependencies:
|
||||
- exiftool (https://www.sno.phy.queensu.ca/~phil/exiftool/)
|
||||
|
||||
--]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(..., {4,0,0})
|
||||
|
||||
gettext.bindtextdomain("fujifilm_ratings", dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("fujifilm_ratings", msgid)
|
||||
end
|
||||
|
||||
local function detect_rating(event, image)
|
||||
if not df.check_if_bin_exists("exiftool") then
|
||||
dt.print_error(_("exiftool not found"))
|
||||
return
|
||||
end
|
||||
local RAF_filename = tostring(image)
|
||||
local JPEG_filename = string.gsub(RAF_filename, "%.RAF$", ".JPG")
|
||||
local command = "exiftool -Rating " .. JPEG_filename
|
||||
dt.print_error(command)
|
||||
local output = io.popen(command)
|
||||
local jpeg_result = output:read("*all")
|
||||
output:close()
|
||||
if string.len(jpeg_result) > 0 then
|
||||
jpeg_result = string.gsub(jpeg_result, "^Rating.*(%d)", "%1")
|
||||
image.rating = tonumber(jpeg_result)
|
||||
dt.print_error(_("Using JPEG Rating: ") .. tostring(jpeg_result))
|
||||
return
|
||||
end
|
||||
command = "exiftool -Rating " .. RAF_filename
|
||||
dt.print_error(command)
|
||||
output = io.popen(command)
|
||||
local raf_result = output:read("*all")
|
||||
output:close()
|
||||
if string.len(raf_result) > 0 then
|
||||
raf_result = string.gsub(raf_result, "^Rating.*(%d)", "%1")
|
||||
image.rating = tonumber(raf_result)
|
||||
dt.print_error(_("Using RAF Rating: ") .. tostring(raf_result))
|
||||
end
|
||||
end
|
||||
|
||||
dt.register_event("post-import-image", detect_rating)
|
||||
|
||||
print(_("fujifilm_ratings loaded."))
|
||||
328
contrib/geoJSON_export.lua
Normal file
328
contrib/geoJSON_export.lua
Normal file
@@ -0,0 +1,328 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright 2016 by Tobias Jakobs.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
darktable geoJSON export script
|
||||
|
||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* mkdir
|
||||
* convert (ImageMagick)
|
||||
* xdg-open
|
||||
* xdg-user-dir
|
||||
|
||||
WARNING
|
||||
This script is only tested with Linux
|
||||
|
||||
USAGE
|
||||
* require this script from your main Lua file
|
||||
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("geoJSON_export",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("geoJSON_export", msgid)
|
||||
end
|
||||
|
||||
-- Sort a table
|
||||
local function spairs(_table, order) -- Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
|
||||
-- collect the keys
|
||||
local keys = {}
|
||||
for _key in pairs(_table) do keys[#keys + 1] = _key end
|
||||
|
||||
-- if order function given, sort by it by passing the table and keys a, b,
|
||||
-- otherwise just sort the keys
|
||||
if order then
|
||||
table.sort(keys, function(a,b) return order(_table, a, b) end)
|
||||
else
|
||||
table.sort(keys)
|
||||
end
|
||||
|
||||
-- return the iterator function
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if keys[i] then
|
||||
return keys[i], _table[keys[i]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
||||
dt.print(string.format(_("Export Image %i/%i"), number, total))
|
||||
end
|
||||
|
||||
local function create_geoJSON_file(storage, image_table, extra_data)
|
||||
if not df.check_if_bin_exists("mkdir") then
|
||||
dt.print_error(_("mkdir not found"))
|
||||
return
|
||||
end
|
||||
if not df.check_if_bin_exists("convert") then
|
||||
dt.print_error(_("convert not found"))
|
||||
return
|
||||
end
|
||||
if not df.check_if_bin_exists("xdg-open") then
|
||||
dt.print_error(_("xdg-open not found"))
|
||||
return
|
||||
end
|
||||
if not df.check_if_bin_exists("xdg-user-dir") then
|
||||
dt.print_error(_("xdg-user-dir not found"))
|
||||
return
|
||||
end
|
||||
|
||||
dt.print_error("Will try to export geoJSON file now")
|
||||
|
||||
local exportDirectory = dt.preferences.read("geoJSON_export","ExportDirectory","string")
|
||||
|
||||
-- Creates dir if not exsists
|
||||
local imageFoldername = "files/"
|
||||
local mkdirCommand = "mkdir -p "..exportDirectory.."/"..imageFoldername
|
||||
dt.control.execute( mkdirCommand)
|
||||
|
||||
-- Create the thumbnails
|
||||
for image,exported_image in pairs(image_table) do
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
local path, filename, filetype = string.match(exported_image, "(.-)([^\\/]-%.?([^%.\\/]*))$")
|
||||
filename = string.upper(string.gsub(filename,"%.%w*", ""))
|
||||
|
||||
-- convert -size 92x92 filename.jpg -resize 92x92 +profile "*" thumbnail.jpg
|
||||
-- In this example, '-size 120x120' gives a hint to the JPEG decoder that the image is going to be downscaled to
|
||||
-- 120x120, allowing it to run faster by avoiding returning full-resolution images to GraphicsMagick for the
|
||||
-- subsequent resizing operation. The '-resize 120x120' specifies the desired dimensions of the output image. It
|
||||
-- will be scaled so its largest dimension is 120 pixels. The '+profile "*"' removes any ICM, EXIF, IPTC, or other
|
||||
-- profiles that might be present in the input and aren't needed in the thumbnail.
|
||||
|
||||
local convertToThumbCommand = "convert -size 96x96 "..exported_image.." -resize 92x92 -mattecolor \"#FFFFFF\" -frame 2x2 +profile \"*\" "..exportDirectory.."/"..imageFoldername.."thumb_"..filename..".jpg"
|
||||
dt.control.execute( convertToThumbCommand)
|
||||
local concertCommand = "convert -size 438x438 "..exported_image.." -resize 438x438 +profile \"*\" "..exportDirectory.."/"..imageFoldername..filename..".jpg"
|
||||
dt.control.execute( concertCommand)
|
||||
end
|
||||
|
||||
-- delete the original image
|
||||
os.remove(exported_image)
|
||||
|
||||
local pattern = "[/]?([^/]+)$"
|
||||
filmName = string.match(image.film.path, pattern)
|
||||
end
|
||||
|
||||
local exportgeoJSONFilename = filmName..".geoJSON"
|
||||
local exportMapBoxHTMLFilename = filmName..".html"
|
||||
|
||||
-- Create the geoJSON file
|
||||
local geoJSON_file = [[
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
]]
|
||||
|
||||
for image,exported_image in pairs(image_table) do
|
||||
--filename = string.upper(string.gsub(image.filename,"%.", "_"))
|
||||
-- Extract filename, e.g DSC9784.ARW -> DSC9784
|
||||
filename = string.upper(string.gsub(image.filename,"%.%w*", ""))
|
||||
-- Extract extension from exported image (user can choose JPG or PNG), e.g DSC9784.JPG -> .JPG
|
||||
extension = string.match(exported_image,"%.%w*$")
|
||||
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
|
||||
local image_title, image_description
|
||||
if (image.title and image.title ~= "") then
|
||||
image_title = image.title
|
||||
else
|
||||
image_title = image.filename
|
||||
end
|
||||
image_description = image.description
|
||||
|
||||
geoJSON_file = geoJSON_file..
|
||||
[[ {
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
]]
|
||||
geoJSON_file = geoJSON_file.." \"coordinates\": ["..string.gsub(tostring(image.longitude),",", ".")..","..string.gsub(tostring(image.latitude),",", ".").."]\n"
|
||||
geoJSON_file = geoJSON_file..
|
||||
[[ },
|
||||
"properties": {
|
||||
"title": "]]..image_title..[[",
|
||||
"description": "]]..image_description..[[",
|
||||
"image": "]]..imageFoldername..filename..[[.jpg",
|
||||
"icon": {
|
||||
"iconUrl": "]]..imageFoldername.."thumb_"..filename..[[.jpg",
|
||||
"iconSize": [96, 65],
|
||||
"iconAnchor": [48, 32],
|
||||
"popupAnchor": [0, -32],
|
||||
"className": "dot"
|
||||
}
|
||||
}
|
||||
}
|
||||
,]]
|
||||
end
|
||||
end
|
||||
|
||||
geoJSON_file = geoJSON_file:sub(0,geoJSON_file:len()-1)
|
||||
geoJSON_file = geoJSON_file..
|
||||
[[
|
||||
]
|
||||
}
|
||||
]]
|
||||
|
||||
|
||||
--HTML
|
||||
if ( dt.preferences.read("geoJSON_export","CreateMapBoxHTMLFile","bool") == true ) then
|
||||
local mapBoxKey = dt.preferences.read("geoJSON_export","mapBoxKey","string")
|
||||
local mapBoxHTML_file = [[
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8 />
|
||||
<title>]]..filmName..[[</title>
|
||||
<script src='https://api.tiles.mapbox.com/mapbox.js/v2.2.4/mapbox.js'></script>
|
||||
<link href='https://api.tiles.mapbox.com/mapbox.js/v2.2.4/mapbox.css' rel='stylesheet' />
|
||||
<style>
|
||||
body { margin:0; padding:0; }
|
||||
.map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id='map-tooltips-js' class='map'> </div>
|
||||
|
||||
<script>
|
||||
L.mapbox.accessToken = ']]..mapBoxKey..[[';
|
||||
var mapTooltipsJS = L.mapbox.map('map-tooltips-js', 'mapbox.light')
|
||||
.setView([0,0], 5);
|
||||
|
||||
var myLayer = L.mapbox.featureLayer()
|
||||
.loadURL(']]..exportgeoJSONFilename..[[')
|
||||
.addTo(mapTooltipsJS);
|
||||
|
||||
// Set a custom icon on each marker based on feature properties.
|
||||
myLayer.on('layeradd', function(e) {
|
||||
var marker = e.layer,
|
||||
feature = marker.feature;
|
||||
marker.setIcon(L.icon(feature.properties.icon));
|
||||
var content = '<h2>'+ feature.properties.title+'<\/h2>' + '<p>'+ feature.properties.description+'<\/p>' + '<img src="'+feature.properties.image+'" alt="">';
|
||||
marker.bindPopup(content,{minWidth: 457});
|
||||
});
|
||||
// Center on click
|
||||
myLayer.on('click', function(e) {
|
||||
mapTooltipsJS.panTo(e.layer.getLatLng());
|
||||
});
|
||||
|
||||
// https://www.mapbox.com/mapbox.js/example/v1.0.0/fit-map-to-markers/
|
||||
// Especially with dynamic data, it's useful to automatically fit all markers
|
||||
// in the map bounds instead of guessing and checking center and zoom values
|
||||
|
||||
// Since this layer is loaded with the asynchronous method loadURL, that uses
|
||||
// AJAX in the background, we wait for all of the markers to be loaded by
|
||||
// waiting for the ready event. If you don't load your markers with an async
|
||||
// method and instead set them with setGeoJSON or similar, you don't need
|
||||
// to do this.
|
||||
myLayer.on('ready', function() {
|
||||
// featureLayer.getBounds() returns the corners of the furthest-out markers,
|
||||
// and map.fitBounds() makes sure that the map contains these.
|
||||
mapTooltipsJS.fitBounds(myLayer.getBounds());
|
||||
});
|
||||
|
||||
// Lines
|
||||
// https://www.mapbox.com/mapbox.js/example/v1.0.0/line-marker/
|
||||
myLayer.on('ready', function(e) {
|
||||
var line = [];
|
||||
|
||||
this.eachLayer(function(marker) {
|
||||
line.push(marker.getLatLng());
|
||||
});
|
||||
|
||||
var polyline_options = {
|
||||
color: '#FF0000'
|
||||
};
|
||||
|
||||
var polyline = L.polyline(line, polyline_options).addTo(mapTooltipsJS);
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
]]
|
||||
|
||||
local file = io.open(exportDirectory.."/"..exportMapBoxHTMLFilename, "w")
|
||||
file:write(mapBoxHTML_file)
|
||||
file:close()
|
||||
end
|
||||
|
||||
local file = io.open(exportDirectory.."/"..exportgeoJSONFilename, "w")
|
||||
file:write(geoJSON_file)
|
||||
file:close()
|
||||
|
||||
dt.print("geoJSON file created in "..exportDirectory)
|
||||
|
||||
-- Open the file with the standard programm
|
||||
if ( dt.preferences.read("geoJSON_export","OpengeoJSONFile","bool") == true ) then
|
||||
local geoJSONFileOpenCommand
|
||||
geoJSONFileOpenCommand = "xdg-open "..exportDirectory.."/\""..exportgeoJSONFilename.."\""
|
||||
dt.control.execute( geoJSONFileOpenCommand)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-- Preferences
|
||||
dt.preferences.register("geoJSON_export",
|
||||
"CreateMapBoxHTMLFile",
|
||||
"bool",
|
||||
_("geoJSON export: Create an additional HTML file"),
|
||||
_("Creates a HTML file, that loads the geoJASON file. (Needs a MapBox key"),
|
||||
false )
|
||||
dt.preferences.register("geoJSON_export",
|
||||
"mapBoxKey",
|
||||
"string",
|
||||
_("geoJSON export: MapBox Key"),
|
||||
_("https://www.mapbox.com/studio/account/tokens"),
|
||||
'' )
|
||||
dt.preferences.register("geoJSON_export",
|
||||
"OpengeoJSONFile",
|
||||
"bool",
|
||||
_("geoJSON export: Open geoJSON file after export"),
|
||||
_("Opens the geoJSON file after the export with the standard program for geoJSON files"),
|
||||
false )
|
||||
|
||||
local handle = io.popen("xdg-user-dir DESKTOP")
|
||||
local result = handle:read()
|
||||
handle:close()
|
||||
if (result == nil) then
|
||||
result = ""
|
||||
end
|
||||
dt.preferences.register("geoJSON_export",
|
||||
"ExportDirectory",
|
||||
"directory",
|
||||
_("geoJSON export: Export directory"),
|
||||
_("A directory that will be used to export the geoJSON files"),
|
||||
result )
|
||||
|
||||
-- Register
|
||||
dt.register_storage("geoJSON_export", "geoJSON Export", nil, create_geoJSON_file)
|
||||
687
contrib/geoToolbox.lua
Normal file
687
contrib/geoToolbox.lua
Normal file
@@ -0,0 +1,687 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2016 Tobias Jakobs
|
||||
|
||||
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/>.
|
||||
]]
|
||||
--[[
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
To do this add this line to the file .config/darktable/luarc:
|
||||
require "geoToolbox"
|
||||
|
||||
* it creates a new geoToolbox lighttable module
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("geoToolbox",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("geoToolbox", msgid)
|
||||
end
|
||||
|
||||
-- <GUI>
|
||||
local labelDistance = dt.new_widget("label")
|
||||
labelDistance.label = _("Distance:")
|
||||
|
||||
local label_copy_gps_lat = dt.new_widget("check_button")
|
||||
{
|
||||
label = _("latitude:"),
|
||||
value = true
|
||||
}
|
||||
local label_copy_gps_lon = dt.new_widget("check_button")
|
||||
{
|
||||
label = _("longitude:"),
|
||||
value = true
|
||||
}
|
||||
local label_copy_gps_ele = dt.new_widget("check_button")
|
||||
{
|
||||
label = _("elevation:"),
|
||||
value = true
|
||||
}
|
||||
-- </GUI>
|
||||
|
||||
local function select_with_gps()
|
||||
local selection = {}
|
||||
job = dt.gui.create_job(_("GPS selection"), true, stop_selection)
|
||||
|
||||
for key,image in ipairs(dt.collection) do
|
||||
if job.valid then
|
||||
job.percent = (key-1)/#dt.collection
|
||||
|
||||
if (image.longitude and image.latitude) then
|
||||
table.insert(selection,image)
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
dt.gui.selection(selection)
|
||||
job.valid = false
|
||||
end
|
||||
|
||||
local function select_without_gps()
|
||||
local selection = {}
|
||||
job = dt.gui.create_job(_("GPS selection"), true, stop_selection)
|
||||
|
||||
for key,image in ipairs(dt.collection) do
|
||||
if job.valid then
|
||||
job.percent = (key-1)/#dt.collection
|
||||
|
||||
if (not image.longitude and not image.latitude) then
|
||||
table.insert(selection,image)
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
dt.gui.selection(selection)
|
||||
job.valid = false
|
||||
end
|
||||
|
||||
local function stop_selection(job)
|
||||
job.valid = false
|
||||
end
|
||||
|
||||
-- This is used for older images in the DB
|
||||
function isnan(x) return x ~= x end
|
||||
|
||||
-- Function from:
|
||||
-- https://forums.coronalabs.com/topic/29019-convert-string-to-date/
|
||||
-- This looks much scarier than it really is. The goal is to turn that string
|
||||
-- into a Unix timestamp (number of seconds since Jan 1, 1970, the standard
|
||||
-- used by most systems). So we use the string.match() method to fetch the
|
||||
-- various date and time parts into their own variables: xyear, xmonth,etc.
|
||||
function make_time_stamp(dateString)
|
||||
local convertedTimestamp
|
||||
--dt.print_error(dateString)
|
||||
if (dateString) then
|
||||
local pattern = "(%d+)%:(%d+)%:(%d+) (%d+):(%d+):(%d+)"
|
||||
local xyear, xmonth, xday, xhour, xminute, xseconds = dateString:match(pattern)
|
||||
--dt.print_error(xyear)
|
||||
convertedTimestamp = os.time({
|
||||
year = xyear,
|
||||
month = xmonth,
|
||||
day = xday,
|
||||
hour = xhour,
|
||||
min = xminute,
|
||||
sec = xseconds})
|
||||
else
|
||||
convertedTimestamp = 0
|
||||
end
|
||||
|
||||
return convertedTimestamp
|
||||
end
|
||||
|
||||
local first_have_data = false
|
||||
local first_latitude = ''
|
||||
local first_longitude = ''
|
||||
local first_elevation = ''
|
||||
local first_image_date = 0
|
||||
|
||||
local function get_first_coordinate()
|
||||
local sel_images = dt.gui.action_images
|
||||
|
||||
first_latitude = ''
|
||||
first_longitude = ''
|
||||
first_elevation = ''
|
||||
first_image_date = 0
|
||||
|
||||
for _,image in ipairs(sel_images) do
|
||||
if not image then
|
||||
first_have_data = false
|
||||
else
|
||||
image_date = image.exif_datetime_taken
|
||||
first_have_data = true
|
||||
if (image.latitude) then
|
||||
first_latitude = image.latitude
|
||||
end
|
||||
if (image.longitude) then
|
||||
first_longitude = image.longitude
|
||||
end
|
||||
if (image.elevation) then
|
||||
first_elevation = image.elevation
|
||||
end
|
||||
if (image.exif_datetime_taken) then
|
||||
first_image_date = make_time_stamp(image.exif_datetime_taken)
|
||||
--dt.print_error(image.exif_datetime_taken)
|
||||
--dt.print_error(first_image_date)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local second_have_data = false
|
||||
local second_latitude = ''
|
||||
local second_longitude = ''
|
||||
local second_elevation = ''
|
||||
local second_image_date = 0
|
||||
|
||||
local function get_second_coordinate()
|
||||
local sel_images = dt.gui.action_images
|
||||
|
||||
second_latitude = ''
|
||||
second_longitude = ''
|
||||
second_elevation = ''
|
||||
second_image_date = 0
|
||||
|
||||
for _,image in ipairs(sel_images) do
|
||||
if not image then
|
||||
second_have_data = false
|
||||
else
|
||||
image_date = image.exif_datetime_taken
|
||||
second_have_data = true
|
||||
if (image.latitude) then
|
||||
second_latitude = image.latitude
|
||||
end
|
||||
if (image.longitude) then
|
||||
second_longitude = image.longitude
|
||||
end
|
||||
if (image.elevation) then
|
||||
second_elevation = image.elevation
|
||||
end
|
||||
if (image.exif_datetime_taken) then
|
||||
second_image_date = make_time_stamp(image.exif_datetime_taken)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local calc_in_between_slider = dt.new_widget("slider")
|
||||
{
|
||||
label = "Position between",
|
||||
soft_min = 0, -- The soft minimum value for the slider, the slider can't go beyond this point
|
||||
soft_max = 100, -- The soft maximum value for the slider, the slider can't go beyond this point
|
||||
hard_min = -100, -- The hard minimum value for the slider, the user can't manually enter a value beyond this point
|
||||
hard_max = 200, -- The hard maximum value for the slider, the user can't manually enter a value beyond this point
|
||||
value = 50 -- The current value of the slider
|
||||
}
|
||||
|
||||
--ToDo: this needs more love
|
||||
local function calc_in_between()
|
||||
local sel_images = dt.gui.action_images
|
||||
for _,image in ipairs(sel_images) do
|
||||
if image then
|
||||
image_date = make_time_stamp(image.exif_datetime_taken)
|
||||
if (first_have_data and second_have_data) then
|
||||
local start_new = 0
|
||||
local end_new = second_image_date - first_image_date
|
||||
local image_date_new = image_date - first_image_date
|
||||
|
||||
local percent_in_between
|
||||
if (end_new == 0) then
|
||||
percent_in_between = 1
|
||||
else
|
||||
percent_in_between = image_date_new/end_new
|
||||
end
|
||||
calc_in_between_slider.value = percent_in_between * 100
|
||||
dt.print_error(percent_in_between)
|
||||
local in_between_latitude = first_latitude + (second_latitude - first_latitude) * percent_in_between
|
||||
local in_between_longitude = first_longitude + (second_longitude - first_longitude) * percent_in_between
|
||||
|
||||
if (first_elevation and second_elevation) then
|
||||
local in_between_elevation = first_elevation + (second_elevation - first_elevation) * percent_in_between
|
||||
|
||||
dt.print_error(first_image_date)
|
||||
dt.print_error(second_image_date)
|
||||
dt.print_error(image_date)
|
||||
dt.print_error(first_elevation)
|
||||
dt.print_error(second_elevation)
|
||||
dt.print_error(in_between_elevation)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local copy_gps_have_data = false
|
||||
local copy_gps_latitude = ''
|
||||
local copy_gps_longitude = ''
|
||||
local copy_gps_elevation = ''
|
||||
|
||||
local function copy_gps()
|
||||
local sel_images = dt.gui.action_images
|
||||
|
||||
copy_gps_latitude = ''
|
||||
copy_gps_longitude = ''
|
||||
copy_gps_elevation = ''
|
||||
|
||||
for _,image in ipairs(sel_images) do
|
||||
if not image then
|
||||
copy_gps_have_data = false
|
||||
else
|
||||
copy_gps_have_data = true
|
||||
if (image.latitude and label_copy_gps_lat.value) then
|
||||
copy_gps_latitude = image.latitude
|
||||
end
|
||||
if (image.longitude and label_copy_gps_lon.value) then
|
||||
copy_gps_longitude = image.longitude
|
||||
end
|
||||
if (image.elevation and label_copy_gps_ele.value) then
|
||||
copy_gps_elevation = image.elevation
|
||||
end
|
||||
end
|
||||
|
||||
label_copy_gps_lat.label = _("latitude: ") .. copy_gps_latitude
|
||||
label_copy_gps_lon.label = _("longitude: ") ..copy_gps_longitude
|
||||
label_copy_gps_ele.label = _("elevation: ") .. copy_gps_elevation
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local function paste_gps(image)
|
||||
local sel_images = dt.gui.action_images
|
||||
|
||||
for _,image in ipairs(sel_images) do
|
||||
if (label_copy_gps_lat.value) then
|
||||
image.latitude = copy_gps_latitude
|
||||
end
|
||||
if (label_copy_gps_lon.value) then
|
||||
image.longitude = copy_gps_longitude
|
||||
end
|
||||
if (label_copy_gps_ele.value) then
|
||||
image.elevation = copy_gps_elevation
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function open_location_in_gnome_maps()
|
||||
|
||||
if not df.check_if_bin_exists("gnome-maps") then
|
||||
dt.print_error(_("gnome-maps not found"))
|
||||
return
|
||||
end
|
||||
|
||||
local sel_images = dt.gui.action_images
|
||||
|
||||
local lat1 = 0;
|
||||
local lon1 = 0;
|
||||
local i = 0;
|
||||
|
||||
-- Use the first image with geo information
|
||||
for _,image in ipairs(sel_images) do
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
lat1 = string.gsub(image.latitude, ",", ".");
|
||||
lon1 = string.gsub(image.longitude, ",", ".");
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local startCommand
|
||||
startCommand = "gnome-maps \"geo:" .. lat1 .. "," .. lon1 .."\""
|
||||
dt.print_error(startCommand)
|
||||
|
||||
dt.control.execute(startCommand)
|
||||
|
||||
end
|
||||
|
||||
-- Trim Funktion from: http://lua-users.org/wiki/StringTrim
|
||||
local function trim12(s)
|
||||
local from = s:match"^%s*()"
|
||||
return from > #s and "" or s:match(".*%S", from)
|
||||
end
|
||||
|
||||
local function reverse_geocode()
|
||||
|
||||
if not df.check_if_bin_exists("curl") then
|
||||
dt.print_error(_("curl not found"))
|
||||
return
|
||||
end
|
||||
|
||||
if not df.check_if_bin_exists("jq") then
|
||||
dt.print_error(_("jq not found"))
|
||||
return
|
||||
end
|
||||
|
||||
local sel_images = dt.gui.selection() --action_images
|
||||
|
||||
local lat1 = 0;
|
||||
local lon1 = 0;
|
||||
local i = 0;
|
||||
|
||||
-- Use the first image with geo information
|
||||
for _,image in ipairs(sel_images) do
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
lat1 = string.gsub(image.latitude, ",", ".");
|
||||
lon1 = string.gsub(image.longitude, ",", ".");
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local startCommand
|
||||
|
||||
local tokan = dt.preferences.read("geoToolbox","mapBoxKey","string")
|
||||
local types = "country";
|
||||
local types = "region";
|
||||
local types = "place";
|
||||
local types = "poi";
|
||||
|
||||
-- MapBox documentation
|
||||
-- https://www.mapbox.com/api-documentation/#retrieve-places-near-a-location
|
||||
-- curl -s, --silent Silent mode (don't output anything)
|
||||
-- jq could be replaced with a Lua JSON parser
|
||||
startCommand = string.format("curl --silent \"https://api.mapbox.com/geocoding/v5/mapbox.places/%s,%s.json?types=%s&access_token=%s\" | jq '.features | .[0] | '.text''", lon1, lat1, types, tokan)
|
||||
|
||||
local handle = io.popen(startCommand)
|
||||
local result = trim12(handle:read("*a"))
|
||||
handle:close()
|
||||
|
||||
-- Errorhandling would be nice
|
||||
--dt.print_error("startCommand: "..startCommand)
|
||||
--dt.print_error("result: '"..result.."'")
|
||||
|
||||
if (result ~= "null") then
|
||||
dt.print(string.sub(result, 2, string.len(result)-2))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- I used code from here:
|
||||
-- http://stackoverflow.com/questions/27928/how-do-i-calculate-distance-between-two-latitude-longitude-points
|
||||
local function get_distance(lat1, lon1, ele1, lat2, lon2, ele2)
|
||||
|
||||
local earthRadius = 6371; -- Radius of the earth in km
|
||||
local dLat = math.rad(lat2-lat1); -- deg2rad below
|
||||
local dLon = math.rad(lon2-lon1);
|
||||
local a =
|
||||
math.sin(dLat/2) * math.sin(dLat/2) +
|
||||
math.cos(math.rad(lat1)) * math.cos(math.rad(lat2)) *
|
||||
math.sin(dLon/2) * math.sin(dLon/2)
|
||||
;
|
||||
local angle = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a));
|
||||
local distance = earthRadius * angle; -- Distance in km
|
||||
|
||||
-- Add the elevation to the calculation
|
||||
local elevation = 0
|
||||
elevation = math.abs(ele1 - ele2) / 1000; --in km
|
||||
|
||||
if (elevation > 0) then
|
||||
distance = math.sqrt(math.pow(elevation,2) + math.pow(distance,2) )
|
||||
end
|
||||
return distance
|
||||
end
|
||||
|
||||
local function calc_distance()
|
||||
|
||||
local lat1 = 0;
|
||||
local lon1 = 0;
|
||||
local lat2 = 0;
|
||||
local lon2 = 0;
|
||||
local ele1 = 0;
|
||||
local ele2 = 0;
|
||||
local i = 0;
|
||||
|
||||
local sel_images = dt.gui.selection()
|
||||
|
||||
for _,image in ipairs(sel_images) do
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
|
||||
i = i + 1;
|
||||
|
||||
if (i == 1) then
|
||||
lat1 = image.latitude
|
||||
lon1 = image.longitude
|
||||
if (image.elevation) then
|
||||
ele1 = image.elevation
|
||||
else
|
||||
ele1 = 0
|
||||
end
|
||||
end
|
||||
|
||||
lat2 = image.latitude
|
||||
lon2 = image.longitude
|
||||
if (image.elevation) then
|
||||
ele2 = image.elevation
|
||||
else
|
||||
ele2 = 0;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local distance = get_distance(lat1, lon1, ele1, lat2, lon2, ele2)
|
||||
|
||||
if (distance < 1) then
|
||||
distance = distance * 1000
|
||||
distanceUnit = _("m")
|
||||
else
|
||||
distanceUnit = _("km")
|
||||
end
|
||||
|
||||
return string.format(_("Distance: %.2f %s"), distance, distanceUnit)
|
||||
end
|
||||
|
||||
local function print_calc_distance()
|
||||
dt.print(calc_distance())
|
||||
end
|
||||
|
||||
local function toolbox_calc_distance()
|
||||
labelDistance.label = calc_distance()
|
||||
end
|
||||
|
||||
local altitude_file_chooser_button = dt.new_widget("file_chooser_button")
|
||||
{
|
||||
title = _("export altitude CSV"), -- The title of the window when choosing a file
|
||||
value = "", -- The currently selected file
|
||||
is_directory = true -- True if the file chooser button only allows directories to be selecte
|
||||
}
|
||||
|
||||
local altitude_filename = dt.new_widget("entry")
|
||||
{
|
||||
text = "altitude.csv",
|
||||
placeholder = "altitude.csv",
|
||||
editable = true,
|
||||
tooltip = _("Name of the exported file"),
|
||||
reset_callback = function(self) self.text = "text" end
|
||||
}
|
||||
|
||||
local function altitude_profile()
|
||||
dt.print(_("Start export"))
|
||||
local sel_images = dt.gui.action_images
|
||||
|
||||
local lat1 = 0;
|
||||
local lon1 = 0;
|
||||
local lat2 = 0;
|
||||
local lon2 = 0;
|
||||
local ele1 = 0;
|
||||
local ele2 = 0;
|
||||
local i = 0;
|
||||
local csv_file = '';
|
||||
csv_file = "km;m".."\n";
|
||||
|
||||
local distance = 0;
|
||||
local distanceFromStart = 0;
|
||||
local elevation = 0;
|
||||
local elevationAdd = 0;
|
||||
|
||||
local sel_images = dt.gui.action_images
|
||||
for _,image in ipairs(sel_images) do
|
||||
if ((not isnan(image.longitude) and not isnan(image.latitude) and not isnan(image.elevation) and image.elevation) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
|
||||
i = i + 1;
|
||||
|
||||
if (i == 1) then
|
||||
lat1 = image.latitude
|
||||
lon1 = image.longitude
|
||||
ele1 = image.elevation
|
||||
else
|
||||
lat1 = lat2
|
||||
lon1 = lon2
|
||||
ele1 = ele2
|
||||
end
|
||||
|
||||
lat2 = image.latitude
|
||||
lon2 = image.longitude
|
||||
ele2 = image.elevation
|
||||
|
||||
if (i == 1) then
|
||||
distanceFromStart = 0
|
||||
else
|
||||
local distance = get_distance(lat1, lon1, ele1, lat2, lon2, ele2)
|
||||
distanceFromStart = distanceFromStart + distance;
|
||||
end
|
||||
|
||||
csv_file = csv_file .. distanceFromStart..";"..image.elevation.."\n";
|
||||
|
||||
end
|
||||
|
||||
end --for
|
||||
|
||||
local exportDirectory = altitude_file_chooser_button.value
|
||||
local exportFilename = altitude_filename.text
|
||||
if (exportFilename == '') then
|
||||
exportFilename = altitude_filename.placeholder
|
||||
end
|
||||
file = io.open(exportDirectory.."/"..exportFilename, "w")
|
||||
file:write(csv_file)
|
||||
file:close()
|
||||
dt.print(_("File created in ")..exportDirectory)
|
||||
|
||||
end
|
||||
|
||||
|
||||
local separator = dt.new_widget("separator"){}
|
||||
local separator2 = dt.new_widget("separator"){}
|
||||
local separator3 = dt.new_widget("separator"){}
|
||||
local separator4 = dt.new_widget("separator"){}
|
||||
local separator5 = dt.new_widget("separator"){}
|
||||
|
||||
dt.register_lib(
|
||||
"geoToolbox", -- Module name
|
||||
"geo toolbox", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
dt.new_widget("box")
|
||||
{
|
||||
orientation = "vertical",
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("select geo images"),
|
||||
tooltip = _("Select all images with GPS information"),
|
||||
clicked_callback = select_with_gps
|
||||
},
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("select non-geo images"),
|
||||
tooltip = _("Select all images without GPS information"),
|
||||
clicked_callback = select_without_gps
|
||||
},
|
||||
separator,--------------------------------------------------------
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("copy GPS data"),
|
||||
tooltip = _("Copy GPS data"),
|
||||
clicked_callback = copy_gps
|
||||
},
|
||||
label_copy_gps_lat,
|
||||
label_copy_gps_lon,
|
||||
label_copy_gps_ele,
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("paste GPS data"),
|
||||
tooltip = _("Paste GPS data"),
|
||||
clicked_callback = paste_gps
|
||||
},
|
||||
separator2,--------------------------------------------------------
|
||||
--ToDo: This need a better UI
|
||||
--[[
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = "get 1st coordinate",
|
||||
tooltip = "Select first image and click this button",
|
||||
clicked_callback = get_first_coordinate
|
||||
},
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = "get 2nd coordinate",
|
||||
tooltip = "Select second image and click this button",
|
||||
clicked_callback = get_second_coordinate
|
||||
},
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = "calc in between",
|
||||
tooltip = "Select third image and click this button",
|
||||
clicked_callback = calc_in_between
|
||||
},
|
||||
calc_in_between_slider,
|
||||
separator3,--------------------------------------------------------
|
||||
]]
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("open in Gnome Maps"),
|
||||
tooltip = _("Open location in Gnome Maps"),
|
||||
clicked_callback = open_location_in_gnome_maps
|
||||
},
|
||||
separator4,--------------------------------------------------------
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("reverse geocode"),
|
||||
tooltip = _("This just shows the name of the location, but doesn't add it as tag"),
|
||||
clicked_callback = reverse_geocode
|
||||
},
|
||||
separator5,--------------------------------------------------------
|
||||
dt.new_widget("label"){label = _("altitude CSV export")},
|
||||
altitude_file_chooser_button,
|
||||
altitude_filename,
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("export altitude CSV file"),
|
||||
tooltip = _("Create an altitude profile using the GPS data in the metadata"),
|
||||
clicked_callback = altitude_profile
|
||||
},
|
||||
labelDistance
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
|
||||
-- Preferences
|
||||
dt.preferences.register("geoToolbox",
|
||||
"mapBoxKey",
|
||||
"string",
|
||||
_("geoToolbox export: MapBox Key"),
|
||||
_("https://www.mapbox.com/studio/account/tokens"),
|
||||
'' )
|
||||
|
||||
-- Register
|
||||
dt.register_event("shortcut", print_calc_distance, _("Calculate the distance from latitude and longitude in km"))
|
||||
dt.register_event("mouse-over-image-changed", toolbox_calc_distance)
|
||||
|
||||
dt.register_event("shortcut", select_with_gps, _("Select all images with GPS information"))
|
||||
dt.register_event("shortcut", select_without_gps, _("Select all images without GPS information"))
|
||||
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
-- kate: hl Lua;
|
||||
302
contrib/gimp.lua
Normal file
302
contrib/gimp.lua
Normal file
@@ -0,0 +1,302 @@
|
||||
--[[
|
||||
|
||||
gimp.lua - export and edit with GIMP
|
||||
|
||||
Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
|
||||
|
||||
Portions are lifted from hugin.lua and thus are
|
||||
|
||||
Copyright (c) 2014 Wolfgang Goetz
|
||||
Copyright (c) 2015 Christian Kanzian
|
||||
Copyright (c) 2015 Tobias Jakobs
|
||||
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
gimp - export an image and open with GIMP for editing
|
||||
|
||||
This script provides another storage (export target) for darktable. Selected
|
||||
images are exported in the specified format to temporary storage. GIMP is launched
|
||||
and opens the files. After editing, the exported images are overwritten to save the
|
||||
changes. When GIMP exits, the exported files are moved into the current collection
|
||||
and imported into the database. The imported files then show up grouped with the
|
||||
originally selected images.
|
||||
|
||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* GIMP - http://www.gimp.org
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
* select an image or images for editing with GIMP
|
||||
* in the export dialog select "Edit with GIMP" and select the format and bit depth for the
|
||||
exported image
|
||||
* Press "export"
|
||||
* Edit the image with GIMP then save the changes with File->Overwrite....
|
||||
* Exit GIMP
|
||||
* The edited image will be imported and grouped with the original image
|
||||
|
||||
CAVEATS
|
||||
* Developed and tested on Ubuntu 14.04 LTS with darktable 2.0.3 and GIMP 2.9.3 (development version with
|
||||
> 8 bit color)
|
||||
* There is no provision for dealing with the xcf files generated by GIMP, since darktable doesn't deal with
|
||||
them. You may want to save the xcf file if you intend on doing further edits to the image or need to save
|
||||
the layers used. Where you save them is up to you.
|
||||
|
||||
BUGS, COMMENTS, SUGGESTIONS
|
||||
* Send to Bill Ferguson, wpferguson@gmail.com
|
||||
|
||||
CHANGES
|
||||
* 20160823 - os.rename doesn't work across filesystems. Added fileCopy and fileMove functions to move the file
|
||||
from the temporary location to the collection location irregardless of what filesystem it is on. If an
|
||||
issue is encountered, a message is printed back to the UI so the user isn't left wondering what happened.
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("gimp",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function split_filepath(str)
|
||||
local result = {}
|
||||
-- Thank you Tobias Jakobs for the awesome regular expression, which I tweaked a little
|
||||
result["path"], result["filename"], result["basename"], result["filetype"] = string.match(str, "(.-)(([^\\/]-)%.?([^%.\\/]*))$")
|
||||
return result
|
||||
end
|
||||
|
||||
local function get_path(str)
|
||||
local parts = split_filepath(str)
|
||||
return parts["path"]
|
||||
end
|
||||
|
||||
local function get_filename(str)
|
||||
local parts = split_filepath(str)
|
||||
return parts["filename"]
|
||||
end
|
||||
|
||||
local function get_basename(str)
|
||||
local parts = split_filepath(str)
|
||||
return parts["basename"]
|
||||
end
|
||||
|
||||
local function get_filetype(str)
|
||||
local parts = split_filepath(str)
|
||||
return parts["filetype"]
|
||||
end
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("gimp", msgid)
|
||||
end
|
||||
|
||||
-- Thanks Tobias Jakobs for the idea and the correction
|
||||
function checkIfFileExists(filepath)
|
||||
local file = io.open(filepath,"r")
|
||||
local ret
|
||||
if file ~= nil then
|
||||
io.close(file)
|
||||
dt.print_error("true checkIfFileExists: "..filepath)
|
||||
ret = true
|
||||
else
|
||||
dt.print_error(filepath.." not found")
|
||||
ret = false
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local function filename_increment(filepath)
|
||||
|
||||
-- break up the filepath into parts
|
||||
local path = get_path(filepath)
|
||||
local basename = get_basename(filepath)
|
||||
local filetype = get_filetype(filepath)
|
||||
|
||||
-- check to see if we've incremented before
|
||||
local increment = string.match(basename, "_(%d-)$")
|
||||
|
||||
if increment then
|
||||
-- we do 2 digit increments so make sure we didn't grab part of the filename
|
||||
if string.len(increment) > 2 then
|
||||
-- we got the filename so set the increment to 01
|
||||
increment = "01"
|
||||
else
|
||||
increment = string.format("%02d", tonumber(increment) + 1)
|
||||
basename = string.gsub(basename, "_(%d-)$", "")
|
||||
end
|
||||
else
|
||||
increment = "01"
|
||||
end
|
||||
local incremented_filepath = path .. basename .. "_" .. increment .. "." .. filetype
|
||||
|
||||
dt.print_error("original file was " .. filepath)
|
||||
dt.print_error("incremented file is " .. incremented_filepath)
|
||||
|
||||
return incremented_filepath
|
||||
end
|
||||
|
||||
local function groupIfNotMember(img, new_img)
|
||||
local image_table = img:get_group_members()
|
||||
local is_member = false
|
||||
for _,image in ipairs(image_table) do
|
||||
dt.print_error(image.filename .. " is a member")
|
||||
if image.filename == new_img.filename then
|
||||
is_member = true
|
||||
dt.print_error("Already in group")
|
||||
end
|
||||
end
|
||||
if not is_member then
|
||||
dt.print_error("group leader is "..img.group_leader.filename)
|
||||
new_img:group_with(img.group_leader)
|
||||
dt.print_error("Added to group")
|
||||
end
|
||||
end
|
||||
|
||||
local function sanitize_filename(filepath)
|
||||
local path = get_path(filepath)
|
||||
local basename = get_basename(filepath)
|
||||
local filetype = get_filetype(filepath)
|
||||
|
||||
local sanitized = string.gsub(basename, " ", "\\ ")
|
||||
|
||||
return path .. sanitized .. "." .. filetype
|
||||
end
|
||||
|
||||
local function show_status(storage, image, format, filename,
|
||||
number, total, high_quality, extra_data)
|
||||
dt.print(string.format(_("Export Image %i/%i"), number, total))
|
||||
end
|
||||
|
||||
local function fileCopy(fromFile, toFile)
|
||||
local result = nil
|
||||
-- if cp exists, use it
|
||||
if df.check_if_bin_exists("cp") then
|
||||
result = os.execute("cp '" .. fromFile .. "' '" .. toFile .. "'")
|
||||
end
|
||||
-- if cp was not present, or if cp failed, then a pure lua solution
|
||||
if not result then
|
||||
local fileIn, err = io.open(fromFile, 'rb')
|
||||
if fileIn then
|
||||
local fileOut, errr = io.open(toFile, 'w')
|
||||
if fileOut then
|
||||
local content = fileIn:read(4096)
|
||||
while content do
|
||||
fileOut:write(content)
|
||||
content = fileIn:read(4096)
|
||||
end
|
||||
result = true
|
||||
fileIn:close()
|
||||
fileOut:close()
|
||||
else
|
||||
dt.print_error("fileCopy Error: " .. errr)
|
||||
end
|
||||
else
|
||||
dt.print_error("fileCopy Error: " .. err)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function fileMove(fromFile, toFile)
|
||||
local success = os.rename(fromFile, toFile)
|
||||
if not success then
|
||||
-- an error occurred, so let's try using the operating system function
|
||||
if df.check_if_bin_exists("mv") then
|
||||
success = os.execute("mv '" .. fromFile .. "' '" .. toFile .. "'")
|
||||
end
|
||||
-- if the mv didn't exist or succeed, then...
|
||||
if not success then
|
||||
-- pure lua solution
|
||||
success = fileCopy(fromFile, toFile)
|
||||
if success then
|
||||
os.remove(fromFile)
|
||||
else
|
||||
dt.print_error("fileMove Error: Unable to move " .. fromFile .. " to " .. toFile .. ". Leaving " .. fromFile .. " in place.")
|
||||
dt.print(string.format(_("Unable to move edited file into collection. Leaving it as %s"), fromFile))
|
||||
end
|
||||
end
|
||||
end
|
||||
return success -- nil on error, some value if success
|
||||
end
|
||||
|
||||
local function gimp_edit(storage, image_table, extra_data) --finalize
|
||||
if not df.check_if_bin_exists("gimp") then
|
||||
dt.print_error(_("GIMP not found"))
|
||||
return
|
||||
end
|
||||
|
||||
-- list of exported images
|
||||
local img_list
|
||||
|
||||
-- reset and create image list
|
||||
img_list = ""
|
||||
|
||||
for _,exp_img in pairs(image_table) do
|
||||
exp_img = sanitize_filename(exp_img)
|
||||
img_list = img_list ..exp_img.. " "
|
||||
end
|
||||
|
||||
dt.print(_("Launching GIMP..."))
|
||||
|
||||
local gimpStartCommand
|
||||
gimpStartCommand = "gimp "..img_list
|
||||
|
||||
dt.print_error(gimpStartCommand)
|
||||
|
||||
dt.control.execute( gimpStartCommand)
|
||||
|
||||
-- for each of the image, exported image pairs
|
||||
-- move the exported image into the directory with the original
|
||||
-- then import the image into the database which will group it with the original
|
||||
-- and then copy over any tags other than darktable tags
|
||||
|
||||
for image,exported_image in pairs(image_table) do
|
||||
|
||||
local myimage_name = image.path .. "/" .. get_filename(exported_image)
|
||||
|
||||
while checkIfFileExists(myimage_name) do
|
||||
myimage_name = filename_increment(myimage_name)
|
||||
-- limit to 99 more exports of the original export
|
||||
if string.match(get_basename(myimage_name), "_(d-)$") == "99" then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
dt.print_error("moving " .. exported_image .. " to " .. myimage_name)
|
||||
local result = fileMove(exported_image, myimage_name)
|
||||
|
||||
if result then
|
||||
dt.print_error("importing file")
|
||||
local myimage = dt.database.import(myimage_name)
|
||||
|
||||
groupIfNotMember(image, myimage)
|
||||
|
||||
for _,tag in pairs(dt.tags.get_tags(image)) do
|
||||
if not (string.sub(tag.name,1,9) == "darktable") then
|
||||
dt.print_error("attaching tag")
|
||||
dt.tags.attach(tag,myimage)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Register
|
||||
dt.register_storage("module_gimp", _("Edit with GIMP"), show_status, gimp_edit)
|
||||
|
||||
--
|
||||
@@ -1,51 +0,0 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright 2014 by Tobias Jakobs.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
darktable select images with or without GPS informations
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
* register a shortcuts
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,1})
|
||||
table = require "table"
|
||||
|
||||
local function selectWithGPS()
|
||||
local selection = {}
|
||||
for _,image in ipairs(dt.database) do
|
||||
if (image.longitude and image.latitude) then
|
||||
table.insert(selection,image)
|
||||
end
|
||||
end
|
||||
dt.gui.selection(selection)
|
||||
end
|
||||
|
||||
local function selectWithoutGPS()
|
||||
local selection = {}
|
||||
for _,image in ipairs(dt.database) do
|
||||
if (not image.longitude and not image.latitude) then
|
||||
table.insert(selection,image)
|
||||
end
|
||||
end
|
||||
dt.gui.selection(selection)
|
||||
end
|
||||
|
||||
dt.register_event("shortcut", selectWithGPS, "Select all images with GPS information")
|
||||
dt.register_event("shortcut", selectWithoutGPS, "Select all images without GPS information")
|
||||
@@ -1,119 +1,112 @@
|
||||
--[[
|
||||
Hugin storage for darktable
|
||||
|
||||
copyright (c) 2014 Wolfgang Goetz
|
||||
copyright (c) 2015 Christian Kanzian
|
||||
copyright (c) 2015 Tobias Jakobs
|
||||
|
||||
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/>.
|
||||
]]
|
||||
|
||||
--[[
|
||||
HUGIN
|
||||
Add a new storage option to send images to hugin.
|
||||
Images are exported to darktable tmp dir first.
|
||||
|
||||
ADDITIANAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* hugin
|
||||
|
||||
USAGE
|
||||
* require this file from your main luarc config file.
|
||||
|
||||
This plugin will add a new storage option and calls hugin after export.
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
|
||||
-- should work with darktable API version 2.0.0
|
||||
dt.configuration.check_version(...,{2,0,0})
|
||||
|
||||
local function checkIfBinExists(bin)
|
||||
local handle = io.popen("which "..bin)
|
||||
local result = handle:read()
|
||||
local ret
|
||||
handle:close()
|
||||
if (result) then
|
||||
dt.print_error("true checkIfBinExists: "..bin)
|
||||
ret = true
|
||||
else
|
||||
dt.print_error(bin.." not found")
|
||||
ret = false
|
||||
end
|
||||
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
local function show_status(storage, image, format, filename,
|
||||
number, total, high_quality, extra_data)
|
||||
dt.print("Export to Hugin "..tostring(number).."/"..tostring(total))
|
||||
end
|
||||
|
||||
local function create_panorama(storage, image_table, extra_data) --finalize
|
||||
if not checkIfBinExists("hugin") then
|
||||
darktable.print_error("hugin not found")
|
||||
return
|
||||
end
|
||||
|
||||
-- Since Hugin 2015.0.0 hugin provides a command line tool to start the assistant
|
||||
-- http://wiki.panotools.org/Hugin_executor
|
||||
-- We need pto_gen to create pto file for hugin_executor
|
||||
-- http://hugin.sourceforge.net/docs/manual/Pto_gen.html
|
||||
|
||||
local hugin_executor = false
|
||||
if (checkIfBinExists("hugin_executor") and checkIfBinExists("pto_gen")) then
|
||||
hugin_executor = true
|
||||
end
|
||||
|
||||
-- list of exported images
|
||||
local img_list
|
||||
|
||||
-- reset and create image list
|
||||
img_list = ""
|
||||
|
||||
for _,v in pairs(image_table) do
|
||||
img_list = img_list ..v.. " "
|
||||
end
|
||||
|
||||
dt.print("Will try to stitch now")
|
||||
|
||||
local huginStartCommand
|
||||
if (hugin_executor) then
|
||||
huginStartCommand = "pto_gen "..img_list.." -o "..dt.configuration.tmp_dir.."/project.pto"
|
||||
dt.print("Creating pto file")
|
||||
coroutine.yield("RUN_COMMAND", huginStartCommand)
|
||||
|
||||
dt.print("Running Assistent")
|
||||
huginStartCommand = "hugin_executor --assistant "..dt.configuration.tmp_dir.."/project.pto"
|
||||
coroutine.yield("RUN_COMMAND", huginStartCommand)
|
||||
|
||||
huginStartCommand = "hugin "..dt.configuration.tmp_dir.."/project.pto"
|
||||
else
|
||||
huginStartCommand = "hugin "..img_list
|
||||
end
|
||||
|
||||
dt.print_error(huginStartCommand)
|
||||
|
||||
if coroutine.yield("RUN_COMMAND", huginStartCommand)
|
||||
then
|
||||
dt.print("Command hugin failed ...")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Register
|
||||
dt.register_storage("module_hugin", "Hugin Panorama", show_status, create_panorama)
|
||||
|
||||
--
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
--[[
|
||||
Hugin storage for darktable
|
||||
|
||||
copyright (c) 2014 Wolfgang Goetz
|
||||
copyright (c) 2015 Christian Kanzian
|
||||
copyright (c) 2015 Tobias Jakobs
|
||||
|
||||
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/>.
|
||||
]]
|
||||
|
||||
--[[
|
||||
HUGIN
|
||||
Add a new storage option to send images to hugin.
|
||||
Images are exported to darktable tmp dir first.
|
||||
|
||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* hugin
|
||||
|
||||
USAGE
|
||||
* require this file from your main luarc config file.
|
||||
|
||||
This plugin will add a new storage option and calls hugin after export.
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
-- works with darktable API version from 2.0.0 to 5.0.0
|
||||
dt.configuration.check_version(...,{2,0,0},{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("hugin",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("hugin", msgid)
|
||||
end
|
||||
|
||||
local function show_status(storage, image, format, filename,
|
||||
number, total, high_quality, extra_data)
|
||||
dt.print("Export to Hugin "..tostring(number).."/"..tostring(total))
|
||||
end
|
||||
|
||||
local function create_panorama(storage, image_table, extra_data) --finalize
|
||||
if not df.check_if_bin_exists("hugin") then
|
||||
dt.print_error(_("hugin not found"))
|
||||
return
|
||||
end
|
||||
|
||||
-- Since Hugin 2015.0.0 hugin provides a command line tool to start the assistant
|
||||
-- http://wiki.panotools.org/Hugin_executor
|
||||
-- We need pto_gen to create pto file for hugin_executor
|
||||
-- http://hugin.sourceforge.net/docs/manual/Pto_gen.html
|
||||
|
||||
local hugin_executor = false
|
||||
if (df.check_if_bin_exists("hugin_executor") and df.check_if_bin_exists("pto_gen")) then
|
||||
hugin_executor = true
|
||||
end
|
||||
|
||||
-- list of exported images
|
||||
local img_list
|
||||
|
||||
-- reset and create image list
|
||||
img_list = ""
|
||||
|
||||
for _,v in pairs(image_table) do
|
||||
img_list = img_list ..v.. " "
|
||||
end
|
||||
|
||||
dt.print(_("Will try to stitch now"))
|
||||
|
||||
local huginStartCommand
|
||||
if (hugin_executor) then
|
||||
huginStartCommand = "pto_gen "..img_list.." -o "..dt.configuration.tmp_dir.."/project.pto"
|
||||
dt.print(_("Creating pto file"))
|
||||
dt.control.execute( huginStartCommand)
|
||||
|
||||
dt.print(_("Running Assistent"))
|
||||
huginStartCommand = "hugin_executor --assistant "..dt.configuration.tmp_dir.."/project.pto"
|
||||
dt.control.execute( huginStartCommand)
|
||||
|
||||
huginStartCommand = "hugin "..dt.configuration.tmp_dir.."/project.pto"
|
||||
else
|
||||
huginStartCommand = "hugin "..img_list
|
||||
end
|
||||
|
||||
dt.print_error(huginStartCommand)
|
||||
|
||||
if not dt.control.execute(huginStartCommand)
|
||||
then
|
||||
dt.print(_("Command hugin failed ..."))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Register
|
||||
dt.register_storage("module_hugin", _("Hugin Panorama"), show_status, create_panorama)
|
||||
|
||||
--
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright 2014 by Tobias Jakobs.
|
||||
Copyright 2016 by Tobias Jakobs.
|
||||
Copyright 2016 by Erik Augustin.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,7 +19,7 @@
|
||||
--[[
|
||||
darktable KML export script
|
||||
|
||||
ADDITIANAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* mkdir
|
||||
* zip (only if you create KMZ files)
|
||||
* convert (ImageMagick)
|
||||
@@ -26,22 +27,37 @@ ADDITIANAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* xdg-user-dir
|
||||
|
||||
WARNING
|
||||
This script is only testet with Linux
|
||||
This script is only tested with Linux
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,1})
|
||||
* require script "official/yield" from your main Lua file in the first line
|
||||
* require this script from your main Lua file
|
||||
* when choosing file format, pick JPEG or PNG as Google Earth doesn't support other formats
|
||||
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("kml_export",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("kml_export", msgid)
|
||||
end
|
||||
|
||||
-- Sort a table
|
||||
local function spairs(_table, order) -- Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
|
||||
-- collect the keys
|
||||
local keys = {}
|
||||
for _key in pairs(_table) do keys[#keys + 1] = _key end
|
||||
|
||||
-- if order function given, sort by it by passing the table and keys a, b,
|
||||
-- otherwise just sort the keys
|
||||
-- otherwise just sort the keys
|
||||
if order then
|
||||
table.sort(keys, function(a,b) return order(_table, a, b) end)
|
||||
else
|
||||
@@ -59,43 +75,120 @@ local function spairs(_table, order) -- Code copied from http://stackoverflow.co
|
||||
end
|
||||
|
||||
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
||||
dt.print("Export Image "..tostring(number).."/"..tostring(total))
|
||||
dt.print(string.format(_("Export Image %i/%i"), number, total))
|
||||
end
|
||||
|
||||
local function checkIfBinExists(bin)
|
||||
local handle = io.popen("which "..bin)
|
||||
local result = handle:read()
|
||||
local ret
|
||||
handle:close()
|
||||
if (not result) then
|
||||
dt.print_error(bin.." not found")
|
||||
ret = false
|
||||
-- Strip accents from a string
|
||||
-- Copied from https://forums.coronalabs.com/topic/43048-remove-special-characters-from-string/
|
||||
function string.stripAccents( str )
|
||||
local tableAccents = {}
|
||||
tableAccents["à"] = "a"
|
||||
tableAccents["á"] = "a"
|
||||
tableAccents["â"] = "a"
|
||||
tableAccents["ã"] = "a"
|
||||
tableAccents["ä"] = "a"
|
||||
tableAccents["ç"] = "c"
|
||||
tableAccents["è"] = "e"
|
||||
tableAccents["é"] = "e"
|
||||
tableAccents["ê"] = "e"
|
||||
tableAccents["ë"] = "e"
|
||||
tableAccents["ì"] = "i"
|
||||
tableAccents["í"] = "i"
|
||||
tableAccents["î"] = "i"
|
||||
tableAccents["ï"] = "i"
|
||||
tableAccents["ñ"] = "n"
|
||||
tableAccents["ò"] = "o"
|
||||
tableAccents["ó"] = "o"
|
||||
tableAccents["ô"] = "o"
|
||||
tableAccents["õ"] = "o"
|
||||
tableAccents["ö"] = "o"
|
||||
tableAccents["ù"] = "u"
|
||||
tableAccents["ú"] = "u"
|
||||
tableAccents["û"] = "u"
|
||||
tableAccents["ü"] = "u"
|
||||
tableAccents["ý"] = "y"
|
||||
tableAccents["ÿ"] = "y"
|
||||
tableAccents["À"] = "A"
|
||||
tableAccents["Á"] = "A"
|
||||
tableAccents["Â"] = "A"
|
||||
tableAccents["Ã"] = "A"
|
||||
tableAccents["Ä"] = "A"
|
||||
tableAccents["Ç"] = "C"
|
||||
tableAccents["È"] = "E"
|
||||
tableAccents["É"] = "E"
|
||||
tableAccents["Ê"] = "E"
|
||||
tableAccents["Ë"] = "E"
|
||||
tableAccents["Ì"] = "I"
|
||||
tableAccents["Í"] = "I"
|
||||
tableAccents["Î"] = "I"
|
||||
tableAccents["Ï"] = "I"
|
||||
tableAccents["Ñ"] = "N"
|
||||
tableAccents["Ò"] = "O"
|
||||
tableAccents["Ó"] = "O"
|
||||
tableAccents["Ô"] = "O"
|
||||
tableAccents["Õ"] = "O"
|
||||
tableAccents["Ö"] = "O"
|
||||
tableAccents["Ù"] = "U"
|
||||
tableAccents["Ú"] = "U"
|
||||
tableAccents["Û"] = "U"
|
||||
tableAccents["Ü"] = "U"
|
||||
tableAccents["Ý"] = "Y"
|
||||
|
||||
local normalizedString = ""
|
||||
|
||||
for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do
|
||||
if tableAccents[strChar] ~= nil then
|
||||
normalizedString = normalizedString..tableAccents[strChar]
|
||||
else
|
||||
normalizedString = normalizedString..strChar
|
||||
end
|
||||
ret = true
|
||||
return ret
|
||||
end
|
||||
|
||||
return normalizedString
|
||||
|
||||
end
|
||||
|
||||
-- Escape XML characters
|
||||
-- Keep & first, otherwise it will double escape other characters
|
||||
-- https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents
|
||||
function string.escapeXmlCharacters( str )
|
||||
|
||||
str = string.gsub(str,"&", "&")
|
||||
str = string.gsub(str,"\"", """)
|
||||
str = string.gsub(str,"'", "'")
|
||||
str = string.gsub(str,"<", "<")
|
||||
str = string.gsub(str,">", ">")
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
local function create_kml_file(storage, image_table, extra_data)
|
||||
|
||||
if not checkIfBinExists("mkdir") then
|
||||
if not df.check_if_bin_exists("mkdir") then
|
||||
dt.print_error(_("mkdir not found"))
|
||||
return
|
||||
end
|
||||
if not checkIfBinExists("convert") then
|
||||
if not df.check_if_bin_exists("convert") then
|
||||
dt.print_error(_("convert not found"))
|
||||
return
|
||||
end
|
||||
if not checkIfBinExists("xdg-open") then
|
||||
if not df.check_if_bin_exists("xdg-open") then
|
||||
dt.print_error(_("xdg-open not found"))
|
||||
return
|
||||
end
|
||||
if not checkIfBinExists("xdg-user-dir") then
|
||||
if not df.check_if_bin_exists("xdg-user-dir") then
|
||||
dt.print_error(_("xdg-user-dir not found"))
|
||||
return
|
||||
end
|
||||
|
||||
dt.print_error("Will try to export KML file now")
|
||||
|
||||
local imageFoldername
|
||||
if ( dt.preferences.read("kml_export","CreateKMZ","bool") == true ) then
|
||||
if not checkIfBinExists("zip") then
|
||||
if not df.check_if_bin_exists("zip") then
|
||||
dt.print_error(_("zip not found"))
|
||||
return
|
||||
end
|
||||
|
||||
exportDirectory = dt.configuration.tmp_dir
|
||||
imageFoldername = ""
|
||||
else
|
||||
@@ -104,62 +197,82 @@ local function create_kml_file(storage, image_table, extra_data)
|
||||
-- Creates dir if not exsists
|
||||
imageFoldername = "files/"
|
||||
local mkdirCommand = "mkdir -p "..exportDirectory.."/"..imageFoldername
|
||||
coroutine.yield("RUN_COMMAND", mkdirCommand)
|
||||
dt.control.execute(mkdirCommand)
|
||||
end
|
||||
|
||||
|
||||
-- Create the thumbnails
|
||||
for _,image in pairs(image_table) do
|
||||
local path, filename, filetype = string.match(image, "(.-)([^\\/]-%.?([^%.\\/]*))$")
|
||||
filename = string.upper(string.gsub(filename,"%.", "_"))
|
||||
|
||||
-- convert -size 92x92 filename.jpg -resize 92x92 +profile "*" thumbnail.jpg
|
||||
-- In this example, '-size 120x120' gives a hint to the JPEG decoder that the image is going to be downscaled to
|
||||
-- 120x120, allowing it to run faster by avoiding returning full-resolution images to GraphicsMagick for the
|
||||
-- subsequent resizing operation. The '-resize 120x120' specifies the desired dimensions of the output image. It
|
||||
-- will be scaled so its largest dimension is 120 pixels. The '+profile "*"' removes any ICM, EXIF, IPTC, or other
|
||||
-- profiles that might be present in the input and aren't needed in the thumbnail.
|
||||
for image,exported_image in pairs(image_table) do
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
local path, filename, filetype = string.match(exported_image, "(.-)([^\\/]-%.?([^%.\\/]*))$")
|
||||
filename = string.upper(string.gsub(filename,"%.%w*", ""))
|
||||
|
||||
local concertToThumbCommand = "convert -size 96x96 "..image.." -resize 92x92 -mattecolor \"#FFFFFF\" -frame 2x2 +profile \"*\" "..exportDirectory.."/"..imageFoldername.."thumb_"..filename..".jpg"
|
||||
-- USE coroutine.yield. It does not block the UI
|
||||
coroutine.yield("RUN_COMMAND", concertToThumbCommand)
|
||||
local concertCommand = "convert -size 438x438 "..image.." -resize 438x438 +profile \"*\" "..exportDirectory.."/"..imageFoldername..filename..".jpg"
|
||||
coroutine.yield("RUN_COMMAND", concertCommand)
|
||||
-- convert -size 92x92 filename.jpg -resize 92x92 +profile "*" thumbnail.jpg
|
||||
-- In this example, '-size 120x120' gives a hint to the JPEG decoder that the image is going to be downscaled to
|
||||
-- 120x120, allowing it to run faster by avoiding returning full-resolution images to GraphicsMagick for the
|
||||
-- subsequent resizing operation. The '-resize 120x120' specifies the desired dimensions of the output image. It
|
||||
-- will be scaled so its largest dimension is 120 pixels. The '+profile "*"' removes any ICM, EXIF, IPTC, or other
|
||||
-- profiles that might be present in the input and aren't needed in the thumbnail.
|
||||
|
||||
local convertToThumbCommand = "convert -size 96x96 "..exported_image.." -resize 92x92 -mattecolor \"#FFFFFF\" -frame 2x2 +profile \"*\" "..exportDirectory.."/"..imageFoldername.."thumb_"..filename..".jpg"
|
||||
dt.control.execute(convertToThumbCommand)
|
||||
else
|
||||
-- Remove exported image if it has no GPS data
|
||||
os.remove(exported_image)
|
||||
end
|
||||
|
||||
local pattern = "[/]?([^/]+)$"
|
||||
filmName = string.match(_.film.path, pattern)
|
||||
filmName = string.match(image.film.path, pattern)
|
||||
|
||||
-- Strip accents from the filename, because GoogleEarth can't open them
|
||||
-- https://github.com/darktable-org/lua-scripts/issues/54
|
||||
filmName = string.stripAccents(filmName)
|
||||
end
|
||||
|
||||
exportKMLFilename = filmName..".kml"
|
||||
exportKMZFilename = filmName..".kmz"
|
||||
|
||||
-- Create the KML file
|
||||
kml_file = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
local kml_file = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
kml_file = kml_file.."<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
|
||||
kml_file = kml_file.."<Document>\n"
|
||||
|
||||
--image_table = dt.gui.selection();
|
||||
kml_file = kml_file.."<name>"..filmName.."</name>\n"
|
||||
kml_file = kml_file.."<name>"..filmName.."</name>\n"
|
||||
kml_file = kml_file.." <description>Exported from darktable</description>\n"
|
||||
|
||||
for image,_ in pairs(image_table) do
|
||||
filename = string.upper(string.gsub(image.filename,"%.", "_"))
|
||||
|
||||
if ((image.longitude and image.latitude) and
|
||||
for image,exported_image in pairs(image_table) do
|
||||
-- Extract filename, e.g DSC9784.ARW -> DSC9784
|
||||
filename = string.upper(string.gsub(image.filename,"%.%w*", ""))
|
||||
-- Extract extension from exported image (user can choose JPG or PNG), e.g DSC9784.JPG -> .JPG
|
||||
extension = string.match(exported_image,"%.%w*$")
|
||||
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
kml_file = kml_file.." <Placemark>\n"
|
||||
kml_file = kml_file.." <name>"..image.filename.."</name>\n"
|
||||
--kml_file = kml_file.." <description></description>\n"
|
||||
|
||||
|
||||
local image_title, image_description
|
||||
if (image.title and image.title ~= "") then
|
||||
image_title = string.escapeXmlCharacters(image.title)
|
||||
else
|
||||
image_title = image.filename
|
||||
end
|
||||
-- Characters should not be escaped in CDATA, but we are using HTML fragment, so we must escape them
|
||||
image_description = string.escapeXmlCharacters(image.description)
|
||||
|
||||
kml_file = kml_file.." <name>"..image_title.."</name>\n"
|
||||
kml_file = kml_file.." <description>"..image_description.."</description>\n"
|
||||
kml_file = kml_file.." <Style>\n"
|
||||
kml_file = kml_file.." <IconStyle>\n"
|
||||
kml_file = kml_file.." <Icon>\n"
|
||||
kml_file = kml_file.." <href>"..imageFoldername.."thumb_"..filename..".jpg</href>\n"
|
||||
kml_file = kml_file.." <href>"..imageFoldername.."thumb_"..filename..".jpg</href>\n"
|
||||
kml_file = kml_file.." </Icon>\n"
|
||||
kml_file = kml_file.." </IconStyle>\n"
|
||||
kml_file = kml_file.." <BalloonStyle>\n"
|
||||
kml_file = kml_file.." <text><![CDATA[<img src=\""..imageFoldername..filename..".jpg\"><br/>]]></text>\n"
|
||||
kml_file = kml_file.." <text><![CDATA[<p><b>"..image_title.."</b></p><img src=\""..imageFoldername..filename..extension.."\"><p>"..image_description.."</p>]]></text>\n"
|
||||
kml_file = kml_file.." <textColor>ff000000</textColor>\n"
|
||||
kml_file = kml_file.." <displayMode>default</displayMode>\n"
|
||||
kml_file = kml_file.." </BalloonStyle>\n"
|
||||
@@ -172,32 +285,36 @@ local function create_kml_file(storage, image_table, extra_data)
|
||||
kml_file = kml_file.." <when>"..string.gsub(image.exif_datetime_taken," ", "T").."Z".."</when>\n"
|
||||
kml_file = kml_file.." </TimeStamp>\n"
|
||||
kml_file = kml_file.." </Point>\n"
|
||||
|
||||
|
||||
kml_file = kml_file.." </Placemark>\n"
|
||||
end
|
||||
end
|
||||
|
||||
-- Connects all images with an path
|
||||
if ( dt.preferences.read("kml_export","CreatePath","bool") == true ) then
|
||||
kml_file = kml_file.." <Placemark>\n"
|
||||
kml_file = kml_file.." <Placemark>\n"
|
||||
kml_file = kml_file.." <name>Path</name>\n" -- ToDo: I think a better name would be nice
|
||||
--kml_file = kml_file.." <description></description>\n"
|
||||
|
||||
kml_file = kml_file.." <Style>\n"
|
||||
kml_file = kml_file.." <LineStyle>\n"
|
||||
kml_file = kml_file.." <Style>\n"
|
||||
kml_file = kml_file.." <LineStyle>\n"
|
||||
kml_file = kml_file.." <color>ff0000ff</color>\n"
|
||||
kml_file = kml_file.." <width>5</width>\n"
|
||||
kml_file = kml_file.." </LineStyle>\n"
|
||||
kml_file = kml_file.." </LineStyle>\n"
|
||||
kml_file = kml_file.." </Style>\n"
|
||||
|
||||
kml_file = kml_file.." <LineString>\n"
|
||||
kml_file = kml_file.." <coordinates>\n"
|
||||
|
||||
for image,_ in spairs(image_table, function(t,a,b) return t[b] < t[a] end) do
|
||||
if ((image.longitude and image.latitude) and
|
||||
|
||||
for image,exported_image in spairs(image_table, function(t,a,b) return t[b] < t[a] end) do
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
kml_file = kml_file.." "..string.gsub(tostring(image.longitude),",", ".")..","..string.gsub(tostring(image.latitude),",", ".")..",0\n"
|
||||
local altitude = 0;
|
||||
if (image.elevation) then
|
||||
altitude = image.elevation;
|
||||
end
|
||||
kml_file = kml_file.." "..string.gsub(tostring(image.longitude),",", ".")..","..string.gsub(tostring(image.latitude),",", ".")..",altitude\n"
|
||||
end
|
||||
end
|
||||
kml_file = kml_file.." </coordinates>\n"
|
||||
@@ -209,7 +326,7 @@ local function create_kml_file(storage, image_table, extra_data)
|
||||
kml_file = kml_file.."</Document>\n"
|
||||
kml_file = kml_file.."</kml>"
|
||||
|
||||
file = io.open(exportDirectory.."/"..exportKMLFilename, "w")
|
||||
local file = io.open(exportDirectory.."/"..exportKMLFilename, "w")
|
||||
file:write(kml_file)
|
||||
file:close()
|
||||
|
||||
@@ -218,38 +335,73 @@ local function create_kml_file(storage, image_table, extra_data)
|
||||
-- Compress the files to create a KMZ file
|
||||
if ( dt.preferences.read("kml_export","CreateKMZ","bool") == true ) then
|
||||
exportDirectory = dt.preferences.read("kml_export","ExportDirectory","string")
|
||||
-- USE coroutine.yield. It does not block the UI
|
||||
|
||||
local createKMZCommand = "zip --test --move --junk-paths "
|
||||
createKMZCommand = createKMZCommand .."\""..exportDirectory.."/"..exportKMZFilename.."\" "
|
||||
createKMZCommand = createKMZCommand .."\""..dt.configuration.tmp_dir.."/"..exportKMLFilename.."\" \""..dt.configuration.tmp_dir.."/"..imageFoldername.."\"*"
|
||||
coroutine.yield("RUN_COMMAND", createKMZCommand)
|
||||
createKMZCommand = createKMZCommand .."\""..exportDirectory.."/"..exportKMZFilename.."\" " -- KMZ filename
|
||||
createKMZCommand = createKMZCommand .."\""..dt.configuration.tmp_dir.."/"..exportKMLFilename.."\" " -- KML file
|
||||
|
||||
for image,exported_image in pairs(image_table) do
|
||||
if ((image.longitude and image.latitude) and
|
||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||
) then
|
||||
local filename = string.upper(string.gsub(image.filename,"%.%w*", ""))
|
||||
|
||||
createKMZCommand = createKMZCommand .."\""..dt.configuration.tmp_dir.."/"..imageFoldername.."thumb_"..filename..".jpg\" " -- thumbnails
|
||||
createKMZCommand = createKMZCommand .."\""..exported_image.."\" " -- images
|
||||
end
|
||||
end
|
||||
|
||||
dt.control.execute(createKMZCommand)
|
||||
end
|
||||
|
||||
-- Open the file with the standard programm
|
||||
-- Open the file with the standard programm
|
||||
if ( dt.preferences.read("kml_export","OpenKmlFile","bool") == true ) then
|
||||
local kmlFileOpenCommand
|
||||
local kmlFileOpenCommand
|
||||
|
||||
if ( dt.preferences.read("kml_export","CreateKMZ","bool") == true ) then
|
||||
kmlFileOpenCommand = "xdg-open "..exportDirectory.."/\""..exportKMZFilename.."\""
|
||||
else
|
||||
kmlFileOpenCommand = "xdg-open "..exportDirectory.."/\""..exportKMLFilename.."\""
|
||||
end
|
||||
coroutine.yield("RUN_COMMAND", kmlFileOpenCommand)
|
||||
end
|
||||
dt.control.execute(kmlFileOpenCommand)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-- Preferences
|
||||
dt.preferences.register("kml_export", "OpenKmlFile", "bool", "KML export: Open KML/KMZ file after export", "Opens the KML file after the export with the standard programm for KML files", false )
|
||||
dt.preferences.register("kml_export",
|
||||
"OpenKmlFile",
|
||||
"bool",
|
||||
_("KML export: Open KML/KMZ file after export"),
|
||||
_("Opens the KML file after the export with the standard program for KML files"),
|
||||
false )
|
||||
|
||||
local handle = io.popen("xdg-user-dir DESKTOP")
|
||||
local result = handle:read()
|
||||
if (result == nil) then
|
||||
result = ""
|
||||
end
|
||||
handle:close()
|
||||
dt.preferences.register("kml_export", "ExportDirectory", "directory", "KML export: Export directory", "A directory that will be used to export the KML/KMZ files", result )
|
||||
dt.preferences.register("kml_export",
|
||||
"ExportDirectory",
|
||||
"directory",
|
||||
_("KML export: Export directory"),
|
||||
_("A directory that will be used to export the KML/KMZ files"),
|
||||
result )
|
||||
|
||||
dt.preferences.register("kml_export", "CreatePath", "bool", "KML export: Connect images with path", "connect all images with a path", false )
|
||||
dt.preferences.register("kml_export", "CreateKMZ", "bool", "KML export: Create KMZ file", "Compress all imeges to one KMZ file", true )
|
||||
dt.preferences.register("kml_export",
|
||||
"CreatePath",
|
||||
"bool",
|
||||
_("KML export: Connect images with path"),
|
||||
_("connect all images with a path"),
|
||||
false )
|
||||
dt.preferences.register("kml_export",
|
||||
"CreateKMZ",
|
||||
"bool",
|
||||
_("KML export: Create KMZ file"),
|
||||
_("Compress all imeges to one KMZ file"),
|
||||
true )
|
||||
|
||||
-- Register
|
||||
dt.register_storage("kml_export", "KML/KMZ Export", nil, create_kml_file)
|
||||
dt.register_storage("kml_export", _("KML/KMZ Export"), nil, create_kml_file)
|
||||
|
||||
94
contrib/passport_guide.lua
Normal file
94
contrib/passport_guide.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
--[[
|
||||
Passport cropping guide for darktable
|
||||
|
||||
copyright (c) 2017 Kåre Hampf
|
||||
|
||||
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/>.
|
||||
]]
|
||||
|
||||
--[[
|
||||
PASSPORT CROPPING GUIDE
|
||||
guides for cropping passport photos based on documents from the Finnish police
|
||||
(https://www.poliisi.fi/instancedata/prime_product_julkaisu/intermin/embeds/poliisiwwwstructure/38462_Passikuvaohje_EN.pdf) describing passport photo dimensions of 47x36 mm and 500x653 px for digital biometric data stored in passports. They use ISO 19794-5 standard based on ICAO 9303 regulations which should also be compliant for all of Europe.
|
||||
|
||||
INSTALLATION
|
||||
* copy this file in $CONFIGDIR/lua/ where CONFIGDIR is your darktable configuration directory
|
||||
* add the following line in the file $CONFIGDIR/luarc
|
||||
require "passport_guide"
|
||||
* (optional) add the line:
|
||||
"plugins/darkroom/clipping/extra_aspect_ratios/passport 36x47mm=47:36"
|
||||
to $CONFIGDIR/darktablerc
|
||||
|
||||
USAGE
|
||||
* when using the cropping tool, select "passport" as guide and if you added the line in yout rc
|
||||
select "passport 36x47mm" as aspect
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{2,0,0},{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("passport_guide",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("passport_guide", msgid)
|
||||
end
|
||||
|
||||
dt.guides.register_guide("passport",
|
||||
-- draw
|
||||
function(cairo, x, y, width, height, zoom_scale)
|
||||
local _width, _height
|
||||
|
||||
-- get the max 36x47 rectangle
|
||||
local aspect_ratio = 47 / 36
|
||||
if width * aspect_ratio > height then
|
||||
_width = height / aspect_ratio
|
||||
_height = height
|
||||
else
|
||||
_width = width
|
||||
_height = width * aspect_ratio
|
||||
end
|
||||
|
||||
cairo:save()
|
||||
|
||||
cairo:translate(x + (width - _width) / 2, y + (height - _height) / 2)
|
||||
cairo:scale(_width / 36, _height / 47)
|
||||
|
||||
-- the outer rectangle
|
||||
cairo:rectangle( 0, 0, 36, 47)
|
||||
|
||||
-- vertical bars
|
||||
cairo:draw_line(16.5, 8, 16.5, 36)
|
||||
cairo:draw_line(19.5, 8, 19.5, 36)
|
||||
|
||||
-- long horisontal bars
|
||||
cairo:draw_line(6, 4, 30, 4)
|
||||
cairo:draw_line(6, 40, 30, 40)
|
||||
|
||||
-- short horisontal bars
|
||||
cairo:draw_line(9, 6, 27, 6)
|
||||
cairo:draw_line(9, 38, 27, 38)
|
||||
|
||||
cairo:restore()
|
||||
end,
|
||||
-- gui
|
||||
function()
|
||||
return dt.new_widget("label"){label = _("ISO 19794-5/ICAO 9309 passport"), halign = "start"}
|
||||
end
|
||||
)
|
||||
|
||||
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on; hl Lua;
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
247
contrib/pdf_slideshow.lua
Normal file
247
contrib/pdf_slideshow.lua
Normal file
@@ -0,0 +1,247 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2017 Pascal Obry
|
||||
|
||||
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/>.
|
||||
]]
|
||||
--[[
|
||||
SELECTION_TO_PDF
|
||||
Generates a PDF slideshow (via Latex) containing all selected images
|
||||
one per slide.
|
||||
|
||||
ADDITIANAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* a PDF-Viewer
|
||||
* pdflatex (Latex)
|
||||
|
||||
USAGE
|
||||
* require this file from your main lua config file:
|
||||
|
||||
This plugin will add a new exporter that will allow you to generate a pdf slideshow.
|
||||
The interface will let you add:
|
||||
- a global title for the slideshow (prefix in all slide label)
|
||||
- a delay for the transition between each slide
|
||||
|
||||
Each slide will contain a single picture with a label at the bottom with the
|
||||
format (all fields can be the empty string):
|
||||
|
||||
<global title> / <image creator> / <image title>
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
|
||||
local gettext = dt.gettext
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("pdf_slideshow",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("pdf_slideshow", msgid)
|
||||
end
|
||||
|
||||
if not df.check_if_bin_exists("pdflatex") then
|
||||
dt.print_error(_("pdflatex not found"))
|
||||
return
|
||||
end
|
||||
|
||||
dt.configuration.check_version(...,{4,0,0},{5,0,0})
|
||||
|
||||
dt.preferences.register
|
||||
("pdf_slideshow","open with","string",
|
||||
_("a pdf viewer"),
|
||||
_("can be an absolute pathname or the tool may be in the PATH"),
|
||||
"xdg-open")
|
||||
|
||||
local title_widget = dt.new_widget("entry") {
|
||||
placeholder=_("slideshow title")
|
||||
}
|
||||
local delay_widget = dt.new_widget("slider")
|
||||
{
|
||||
label = _("transition delay (s)"),
|
||||
soft_min = 1, -- The soft minimum value for the slider, the slider can't go beyond this point
|
||||
soft_max = 20, -- The soft maximum value for the slider, the slider can't go beyond this point
|
||||
hard_min = 1, -- The hard minimum value for the slider, the user can't manually enter a value beyond this point
|
||||
hard_max = 60, -- The hard maximum value for the slider, the user can't manually enter a value beyond this point
|
||||
value = 5, -- The current value of the slider
|
||||
step = 1,
|
||||
digits = 0
|
||||
}
|
||||
local check_button_title = dt.new_widget("check_button")
|
||||
{
|
||||
label = _('include image title'),
|
||||
value = true,
|
||||
tooltip = _('whether to include the image title (if defined) into the slide')
|
||||
}
|
||||
local check_button_author = dt.new_widget("check_button")
|
||||
{
|
||||
label = _('include image author'),
|
||||
value = true,
|
||||
tooltip = _('whether to include the image author (if defined) into the slide')
|
||||
}
|
||||
local widget = dt.new_widget("box") {
|
||||
orientation=horizontal,
|
||||
dt.new_widget("label"){label = _("slideshow title")},
|
||||
title_widget,
|
||||
dt.new_widget("label"){label = ""},
|
||||
delay_widget,
|
||||
check_button_author,
|
||||
check_button_title
|
||||
}
|
||||
|
||||
local ending = [[
|
||||
\end{document}
|
||||
]]
|
||||
|
||||
-- only jpeg files are supported by latex
|
||||
local function support_format(storage, format)
|
||||
fmt = string.lower(format.name)
|
||||
if string.match(fmt,"jpeg") ~= nil and string.match(fmt,"2000") == nil then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local filename =dt.configuration.tmp_dir.."/pdfout.tex"
|
||||
|
||||
local function my_write(latexfile,arg)
|
||||
local res,errmsg = latexfile:write(arg)
|
||||
if not res then
|
||||
error(errmsg)
|
||||
end
|
||||
end
|
||||
|
||||
local function img_filename(file,i)
|
||||
local dir=string.gsub(file, "(.*)(%/.*%..*)", "%1")
|
||||
local ext=string.gsub(file, "(.*)(%..*)", "%2")
|
||||
local lfile = dir.."/slide"..i..ext
|
||||
return lfile
|
||||
end
|
||||
|
||||
local function slide(latexfile,i,image,file)
|
||||
local title = ""
|
||||
if title_widget.text ~= "" then
|
||||
title = title_widget.text
|
||||
end
|
||||
if check_button_author.value == true and image.creator ~= "" then
|
||||
if title ~= "" then
|
||||
title = title.." / "
|
||||
end
|
||||
title = title..image.creator
|
||||
end
|
||||
if check_button_title.value == true and image.title ~= "" then
|
||||
if title ~= "" then
|
||||
title = title.." / "
|
||||
end
|
||||
title = title..image.title
|
||||
end
|
||||
|
||||
-- make sure there is no non-printable characters in the title
|
||||
title = string.gsub(title, "\n", "")
|
||||
title = string.gsub(title, "\r", "")
|
||||
title = string.gsub(title, "\t", "")
|
||||
|
||||
local lfile = img_filename(file,i)
|
||||
os.rename(file, lfile)
|
||||
|
||||
my_write(latexfile,"\\begin{figure}\n")
|
||||
my_write(latexfile,"\\begin{measuredfigure}\n")
|
||||
my_write(latexfile,"\\includegraphics[height=0.92\\textheight]{"..lfile.."}\n")
|
||||
my_write(latexfile,"\\end{measuredfigure}\n")
|
||||
my_write(latexfile,"\\caption{{\\color{white}"..title.."}}\n")
|
||||
my_write(latexfile,"\\end{figure}\n")
|
||||
end
|
||||
|
||||
dt.register_storage("pdf_slideshow",_("pdf slideshow"),
|
||||
nil,
|
||||
function(storage,image_table)
|
||||
|
||||
local preamble = [[
|
||||
\documentclass[a4paper,12pt,landscape]{beamer}
|
||||
\usetheme{default}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{threeparttable}
|
||||
\usepackage[space]{grffile} % needed to support filename with spaces
|
||||
\pagestyle{empty}
|
||||
\parindent0pt
|
||||
\usepackage{geometry}
|
||||
\usepackage{color}
|
||||
\pagecolor{black!100}
|
||||
\color{white}
|
||||
\geometry{a4paper,landscape,left=2mm,right=2mm, top=2mm, bottom=2mm}
|
||||
\mode<presentation>
|
||||
\transduration{]]..string.gsub(delay_widget.value, ",", ".")..[[}
|
||||
\setbeamertemplate{footline}{}
|
||||
\setbeamertemplate{headline}{}
|
||||
\setbeamertemplate{frametitle}{}
|
||||
\setbeamertemplate{navigation symbols}{}
|
||||
\setbeamercolor{background canvas}{fg=white,bg=black!100}
|
||||
\setbeamercolor{normal text}{fg=white}
|
||||
\hypersetup{pdfstartpage=1,pdfpagemode=FullScreen}
|
||||
\setbeamertemplate{caption}{\insertcaption}
|
||||
\begin{document}
|
||||
]]
|
||||
|
||||
local errmsg
|
||||
local latexfile
|
||||
latexfile,errmsg=io.open(filename,"w")
|
||||
if not latexfile then
|
||||
error(errmsg)
|
||||
end
|
||||
my_write(latexfile,preamble)
|
||||
local i = 1
|
||||
for img,file in pairs(image_table) do
|
||||
slide(latexfile,i,img,file)
|
||||
my_write(latexfile,"\n\\bigskip\n")
|
||||
i = i+1
|
||||
end
|
||||
my_write(latexfile,ending)
|
||||
latexfile:close()
|
||||
|
||||
-- convert to PDF
|
||||
local dir=string.gsub(filename, "(.*/)(.*)", "%1")
|
||||
local locfile=string.gsub(filename, "(.*/)(.*)", "%2")
|
||||
local command = "pdflatex -halt-on-error -output-directory "..dir.." "..locfile
|
||||
local result = dt.control.execute(command)
|
||||
if result ~= 0 then
|
||||
dt.print(_("problem running pdflatex")) -- this one is probably usefull to the user
|
||||
error(_("problem running ")..command)
|
||||
end
|
||||
|
||||
-- open the PDF
|
||||
local pdffile=string.gsub(filename, ".tex", ".pdf")
|
||||
command = dt.preferences.read("pdf_slideshow","open with","string")
|
||||
command = command.." "..pdffile
|
||||
local result = dt.control.execute(command)
|
||||
if result ~= 0 then
|
||||
dt.print(_("problem running pdf viewer")) -- this one is probably usefull to the user
|
||||
error(_("problem running ")..command)
|
||||
end
|
||||
|
||||
-- finally do some clean-up
|
||||
local i = 1
|
||||
for img,file in pairs(image_table) do
|
||||
local lfile = img_filename(file,i)
|
||||
os.remove(lfile)
|
||||
i = i+1
|
||||
end
|
||||
end,
|
||||
support_format,
|
||||
nil,
|
||||
widget)
|
||||
|
||||
--
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
251
contrib/quicktag.lua
Normal file
251
contrib/quicktag.lua
Normal file
@@ -0,0 +1,251 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright (C) 2017 by Christian Kanzian
|
||||
|
||||
Thanks to
|
||||
Copyright (C) 2016 Bill Ferguson
|
||||
Copyright (C) 2017 Tobias Jakobs
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
|
||||
--[[
|
||||
QUICKTAG
|
||||
For faster attaching your favorite tags, the script adds shortcuts and
|
||||
the module "quicktag" in lighttable mode with a changeable number of buttons.
|
||||
To each button a tag can be assigned. If the tags do not exist in your database,
|
||||
they are added to the database once they get the first time attached to an image.
|
||||
|
||||
The number of buttons/shortcuts can be changed in the lua preferences.
|
||||
Changes in the number require a restart of darktable.
|
||||
|
||||
INSTALATION
|
||||
* copy this file in $CONFIGDIR/lua/ where CONFIGDIR is your darktable configuration directory
|
||||
* add the following line in the file $CONFIGDIR/luarc require "quicktag"
|
||||
|
||||
USAGE
|
||||
* set the number of quicktags between 2 and 10 in the preferences dialog and restart darktable
|
||||
* if wanted set the shortcuts in the preferences dialog
|
||||
* to add or change a quicktag, first select the old tag with the combobox "old quicktag",
|
||||
enter a new tag in the "new quicktag" filed and press "set quicktag"
|
||||
* use a shortcut or button to attach the tag to selected images
|
||||
]]
|
||||
|
||||
|
||||
local dt = require "darktable"
|
||||
local debug = require "darktable.debug"
|
||||
|
||||
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("quicktag",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("quicktag", msgid)
|
||||
end
|
||||
|
||||
-- maximum length of button labels
|
||||
dt.preferences.register("quickTag",
|
||||
"labellength",
|
||||
"integer", -- type
|
||||
_("max. length of button labels"), -- label
|
||||
_("may range from 15 to 60 - needs a restart"), -- tooltip
|
||||
25, -- default
|
||||
15, -- min
|
||||
60)
|
||||
|
||||
|
||||
|
||||
local max_label_length = dt.preferences.read("quickTag", "labellength", "integer")
|
||||
|
||||
-- register number of quicktags
|
||||
dt.preferences.register("quickTag",
|
||||
"quickTagnumber",
|
||||
"integer", -- type
|
||||
_("number of quicktag fields"), -- label
|
||||
_("may range from 2 to 20 - needs a restart"), -- tooltip
|
||||
5, -- default
|
||||
2, -- min
|
||||
20)
|
||||
|
||||
local qnr = dt.preferences.read("quickTag", "quickTagnumber", "integer")
|
||||
|
||||
|
||||
|
||||
-- get quicktags from the preferences
|
||||
local quicktag_table = {}
|
||||
|
||||
|
||||
local function read_pref_tags()
|
||||
for j=1,qnr do
|
||||
quicktag_table[j] = dt.preferences.read("quickTag", "quicktag"..j, "string")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local quicktag_label = {}
|
||||
|
||||
|
||||
local function abbrevate_tags(t)
|
||||
for i=1,qnr do
|
||||
local taglength = string.len(t[i])
|
||||
if taglength > max_label_length then
|
||||
local max_2 = math.floor(max_label_length/2)
|
||||
quicktag_label[i] = string.sub(t[i],0,max_2).."..."..string.sub(t[i],taglength - max_2)
|
||||
else
|
||||
quicktag_label[i] = t[i]
|
||||
end
|
||||
-- print(quicktag_label[i])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- quicktag function to attach tags
|
||||
local function tagattach(tag,qtagnr)
|
||||
if tag == "" then
|
||||
dt.print(string.format(_("quicktag %i is empty, please set a tag"), qtagnr))
|
||||
return true
|
||||
end
|
||||
|
||||
local tagnr = dt.tags.find(tag)
|
||||
|
||||
--create tag if it does not exist
|
||||
if tagnr == nil then
|
||||
dt.tags.create(tag)
|
||||
tagnr = dt.tags.find(tag)
|
||||
end
|
||||
|
||||
local sel_images = dt.gui.action_images
|
||||
|
||||
if next(sel_images) == nil then
|
||||
dt.print(_("no images selected"))
|
||||
return true
|
||||
end
|
||||
|
||||
local counter = 0
|
||||
|
||||
for _,image in ipairs(sel_images) do
|
||||
dt.tags.attach(tagnr,image)
|
||||
counter = counter+1
|
||||
end
|
||||
dt.print(string.format(_("tag \"%s\" attached to %i image(s)"), tag, counter))
|
||||
end
|
||||
|
||||
|
||||
-- create quicktag module elements
|
||||
-- read tags from preferences for initialization
|
||||
read_pref_tags()
|
||||
abbrevate_tags(quicktag_table)
|
||||
|
||||
|
||||
local button = {}
|
||||
|
||||
-- function to create buttons with tags as labels
|
||||
for j=1,qnr do
|
||||
button[#button+1] = dt.new_widget("button") {
|
||||
label = j..": "..quicktag_table[j],
|
||||
clicked_callback = function() tagattach(tostring(quicktag_table[j]),j) end}
|
||||
end
|
||||
|
||||
|
||||
local old_quicktag = dt.new_widget("combobox"){
|
||||
label = _("old tag"),
|
||||
tooltip = _("select the quicktag to replace")
|
||||
}
|
||||
|
||||
local function update_quicktag_list()
|
||||
-- abrevate long tags for the labels
|
||||
abbrevate_tags(quicktag_table)
|
||||
for j=1,qnr do
|
||||
button[j].label = j..": "..quicktag_label[j]
|
||||
old_quicktag[j] = j..": "..quicktag_table[j]
|
||||
end
|
||||
end
|
||||
|
||||
update_quicktag_list()
|
||||
|
||||
local new_quicktag = dt.new_widget("entry"){
|
||||
text = "",
|
||||
placeholder = _("new tag"),
|
||||
is_password = true,
|
||||
editable = true,
|
||||
tooltip = _("enter your tag here")
|
||||
}
|
||||
|
||||
local set_quicktag_button = dt.new_widget("button") {
|
||||
label = _("set tag"),
|
||||
clicked_callback = function()
|
||||
local old_tag = quicktag_table[old_quicktag.selected]
|
||||
if new_quicktag.text == "" then
|
||||
dt.print(_("new quicktag is empty!"))
|
||||
else
|
||||
quicktag_table[old_quicktag.selected] = new_quicktag.text
|
||||
dt.preferences.write("quickTag", "quicktag"..old_quicktag.selected, "string", new_quicktag.text)
|
||||
dt.print(string.format(_("quicktag \"%s\" replaced by \"%s\""), old_tag, new_quicktag.text))
|
||||
update_quicktag_list()
|
||||
new_quicktag.text = ""
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
local new_qt_widget = dt.new_widget ("box") {
|
||||
orientation = "horizontal",
|
||||
dt.new_widget("label") { label = _("new tag") },
|
||||
new_quicktag,
|
||||
set_quicktag_button
|
||||
}
|
||||
|
||||
|
||||
-- back UI elements in a table
|
||||
-- thanks to wpferguson for the hint
|
||||
local widget_table = {}
|
||||
|
||||
for i=1,qnr do
|
||||
widget_table[#widget_table + 1] = button[i]
|
||||
end
|
||||
|
||||
widget_table[#widget_table + 1] = dt.new_widget("separator"){}
|
||||
widget_table[#widget_table + 1] = old_quicktag
|
||||
widget_table[#widget_table + 1] = new_qt_widget
|
||||
|
||||
|
||||
--create module
|
||||
dt.register_lib(
|
||||
"quicktag", -- Module name
|
||||
"quicktag", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 490}},
|
||||
|
||||
dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
table.unpack(widget_table),
|
||||
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
|
||||
-- create shortcuts
|
||||
for i=1,qnr do
|
||||
dt.register_event("shortcut",
|
||||
function(event, shortcut) tagattach(tostring(quicktag_table[i])) end,
|
||||
string.format(_("quicktag %i"),i))
|
||||
end
|
||||
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;
|
||||
87
contrib/rate_group.lua
Normal file
87
contrib/rate_group.lua
Normal file
@@ -0,0 +1,87 @@
|
||||
--[[
|
||||
|
||||
Rate Group:
|
||||
|
||||
Script to provide shortcuts for rating or rejecting all images within a group;
|
||||
particularly useful for RAW+JPEG shooters employing a star rating workflow
|
||||
like the below:
|
||||
|
||||
http://blog.chasejarvis.com/blog/2011/03/photo-editing-101/
|
||||
|
||||
|
||||
Installation and usage:
|
||||
|
||||
1. Copy this file into ~/.config/darktable/lua/
|
||||
|
||||
2. Add `require "rate_group"` to new line in ~/.config/darktable/luarc
|
||||
|
||||
3. Restart darktable
|
||||
|
||||
4. Assign a keyboard shortcut to each action via settings > shortcuts > lua
|
||||
|
||||
I use the following shortcuts:
|
||||
|
||||
* Reject group: Ctrl+R
|
||||
* Rate group 1: Ctrl+1
|
||||
* Rate group 2: Ctrl+2
|
||||
* Rate group 3: Ctrl+3
|
||||
* Rate group 4: Ctrl+4
|
||||
* Rate group 5: Ctrl+5
|
||||
* Rate group 0: Ctrl+0
|
||||
|
||||
|
||||
Author: Dom H (dom@hxy.io)
|
||||
License: GPLv2
|
||||
|
||||
This script is based on Thibault Jouannic's `reject_group` script:
|
||||
http://redmine.darktable.org/issues/8968#note-20
|
||||
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
|
||||
-- added version check
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0})
|
||||
|
||||
local function apply_rating(rating)
|
||||
local images = dt.gui.action_images
|
||||
for _, i in ipairs(images) do
|
||||
local members = i:get_group_members()
|
||||
for _, m in ipairs(members) do
|
||||
m.rating = rating
|
||||
end
|
||||
end
|
||||
if rating < 0 then
|
||||
dt.print("rejecting group(s)")
|
||||
else
|
||||
dt.print("applying rating " ..rating.. " to group(s)")
|
||||
end
|
||||
end
|
||||
|
||||
dt.register_event("shortcut",function(event, shortcut)
|
||||
apply_rating(-1)
|
||||
end, "Reject group")
|
||||
|
||||
dt.register_event("shortcut",function(event, shortcut)
|
||||
apply_rating(0)
|
||||
end, "Rate group 0")
|
||||
|
||||
dt.register_event("shortcut",function(event, shortcut)
|
||||
apply_rating(1)
|
||||
end, "Rate group 1")
|
||||
|
||||
dt.register_event("shortcut",function(event, shortcut)
|
||||
apply_rating(2)
|
||||
end, "Rate group 2")
|
||||
|
||||
dt.register_event("shortcut",function(event, shortcut)
|
||||
apply_rating(3)
|
||||
end, "Rate group 3")
|
||||
|
||||
dt.register_event("shortcut",function(event, shortcut)
|
||||
apply_rating(4)
|
||||
end, "Rate group 4")
|
||||
|
||||
dt.register_event("shortcut",function(event, shortcut)
|
||||
apply_rating(5)
|
||||
end, "Rate group 5")
|
||||
114
contrib/rename-tags.lua
Normal file
114
contrib/rename-tags.lua
Normal file
@@ -0,0 +1,114 @@
|
||||
--[[
|
||||
Rename tags
|
||||
|
||||
AUTHOR
|
||||
Sebastian Witt (se.witt@gmx.net)
|
||||
|
||||
INSTALLATION
|
||||
* copy this file in $CONFIGDIR/lua/ where CONFIGDIR
|
||||
is your darktable configuration directory
|
||||
* add the following line in the file $CONFIGDIR/luarc
|
||||
require "rename-tags"
|
||||
|
||||
USAGE
|
||||
* In lighttable there is a new entry: 'rename tag'
|
||||
* Enter old tag (this one gets deleted!)
|
||||
* Enter new tag name
|
||||
|
||||
LICENSE
|
||||
GPLv2
|
||||
|
||||
]]
|
||||
|
||||
local darktable = require "darktable"
|
||||
local debug = require "darktable.debug"
|
||||
|
||||
-- GUI entries
|
||||
local old_tag = darktable.new_widget("entry") { tooltip = "Enter old tag" }
|
||||
local new_tag = darktable.new_widget("entry") { tooltip = "Enter new tag" }
|
||||
|
||||
-- This function does the renaming
|
||||
local function rename_tags(widget)
|
||||
-- If entries are empty, return
|
||||
if old_tag.text == '' then
|
||||
darktable.print ("Old tag can't be empty")
|
||||
return
|
||||
end
|
||||
if new_tag.text == '' then
|
||||
darktable.print ("New tag can't be empty")
|
||||
return
|
||||
end
|
||||
|
||||
local Count = 0
|
||||
|
||||
-- Check if old tag exists
|
||||
local ot = darktable.tags.find (old_tag.text)
|
||||
|
||||
if not ot then
|
||||
darktable.print ("Old tag does not exist")
|
||||
return
|
||||
end
|
||||
|
||||
-- Show job
|
||||
local job = darktable.gui.create_job ("Renaming tag", true)
|
||||
|
||||
old_tag.editable = false
|
||||
new_tag.editable = false
|
||||
|
||||
-- Create if it does not exists
|
||||
local nt = darktable.tags.create (new_tag.text)
|
||||
|
||||
-- Search images for old tag
|
||||
dbcount = #darktable.database
|
||||
for i,image in ipairs(darktable.database) do
|
||||
-- Update progress bar
|
||||
job.percent = i / dbcount
|
||||
|
||||
local tags = image:get_tags ()
|
||||
for _,t in ipairs (tags) do
|
||||
if t.name == old_tag.text then
|
||||
-- Found it, attach new tag
|
||||
image:attach_tag (nt)
|
||||
Count = Count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Delete old tag, this removes it from all images
|
||||
darktable.tags.delete (ot)
|
||||
|
||||
job.valid = false
|
||||
darktable.print ("Renamed tags for " .. Count .. " images")
|
||||
old_tag.editable = true
|
||||
new_tag.editable = true
|
||||
end
|
||||
|
||||
-- GUI
|
||||
local old_widget = darktable.new_widget ("box") {
|
||||
orientation = "horizontal",
|
||||
darktable.new_widget("label") { label = "Old tag" },
|
||||
old_tag
|
||||
}
|
||||
|
||||
local new_widget = darktable.new_widget ("box") {
|
||||
orientation = "horizontal",
|
||||
darktable.new_widget("label") { label = "New tag" },
|
||||
new_tag
|
||||
}
|
||||
|
||||
local function rename_reset(widget)
|
||||
old_tag.text = ''
|
||||
new_tag.text = ''
|
||||
end
|
||||
|
||||
local rename_widget = darktable.new_widget ("box") {
|
||||
orientation = "vertical",
|
||||
reset_callback = rename_reset,
|
||||
old_widget,
|
||||
new_widget,
|
||||
darktable.new_widget("button") { label = "Go", clicked_callback = rename_tags }
|
||||
}
|
||||
|
||||
|
||||
darktable.register_lib ("rename_tags", "rename tag", true, true, {[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 20},}, rename_widget, nil, nil)
|
||||
|
||||
64
contrib/select_untagged.lua
Normal file
64
contrib/select_untagged.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2017 Jannis_V
|
||||
|
||||
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/>.
|
||||
]]
|
||||
|
||||
--[[
|
||||
Enable selection of untagged images (darktable|* tags are ignored)
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("select_untagged",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("select_untagged", msgid)
|
||||
end
|
||||
|
||||
local function stop_job(job)
|
||||
job.valid = false
|
||||
end
|
||||
|
||||
local function select_untagged_images()
|
||||
job = dt.gui.create_job(_("select untagged images"), true, stop_job)
|
||||
|
||||
local selection = {}
|
||||
|
||||
for key,image in ipairs(dt.collection) do
|
||||
if(job.valid) then
|
||||
job.percent = (key-1)/#dt.collection
|
||||
local tags = dt.tags.get_tags(image)
|
||||
local hasTags = false
|
||||
for _,tag in ipairs(tags) do
|
||||
if not string.match(tag.name,"darktable|") then
|
||||
hasTags = true
|
||||
end
|
||||
end
|
||||
if hasTags == false then
|
||||
table.insert(selection,image)
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
job.valid = false
|
||||
dt.gui.selection(selection)
|
||||
end
|
||||
|
||||
dt.gui.libs.select.register_selection(_("select untagged"),select_untagged_images,_("select all images containing no tags or only tags added by darktable"))
|
||||
@@ -23,21 +23,19 @@ You need rhythmbox-client installed to use this script
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,2})
|
||||
|
||||
local function checkIfBinExists(bin)
|
||||
local handle = io.popen("which "..bin)
|
||||
local result = handle:read()
|
||||
local ret
|
||||
handle:close()
|
||||
if (not result) then
|
||||
dt.print_error(bin.." not found")
|
||||
ret = false
|
||||
end
|
||||
ret = true
|
||||
return ret
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{2,0,2},{3,0,0},{4,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("slideshowMusic",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("slideshowMusic", msgid)
|
||||
end
|
||||
|
||||
local function playSlideshowMusic(_, old_view, new_view)
|
||||
@@ -46,7 +44,8 @@ local function playSlideshowMusic(_, old_view, new_view)
|
||||
filename = dt.preferences.read("slideshowMusic","SlideshowMusic","string")
|
||||
playMusic = dt.preferences.read("slideshowMusic","PlaySlideshowMusic","bool")
|
||||
|
||||
if not checkIfBinExists("rhythmbox-client") then
|
||||
if not df.check_if_bin_exists("rhythmbox-client") then
|
||||
dt.print_error(_("rhythmbox-client not found"))
|
||||
return
|
||||
end
|
||||
|
||||
@@ -55,26 +54,26 @@ local function playSlideshowMusic(_, old_view, new_view)
|
||||
|
||||
if (new_view.id == "slideshow") then
|
||||
playCommand = 'rhythmbox-client --play-uri="'..filename..'"'
|
||||
|
||||
|
||||
--dt.print_error(playCommand)
|
||||
coroutine.yield("RUN_COMMAND", playCommand)
|
||||
dt.control.execute( playCommand)
|
||||
else
|
||||
if (old_view.id == "slideshow") then
|
||||
if (old_view and old_view.id == "slideshow") then
|
||||
stopCommand = "rhythmbox-client --pause"
|
||||
--dt.print_error(stopCommand)
|
||||
coroutine.yield("RUN_COMMAND", stopCommand)
|
||||
dt.control.execute( stopCommand)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Preferences
|
||||
dt.preferences.register("slideshowMusic", "SlideshowMusic", "file", "Slideshow background music file", "", "")
|
||||
dt.preferences.register("slideshowMusic",
|
||||
dt.preferences.register("slideshowMusic", "SlideshowMusic", "file", _("Slideshow background music file"), "", "")
|
||||
dt.preferences.register("slideshowMusic",
|
||||
"PlaySlideshowMusic",
|
||||
"bool",
|
||||
"Play slideshow background music",
|
||||
"Plays music with rhythmbox if a slideshow starts",
|
||||
_("Play slideshow background music"),
|
||||
_("Plays music with rhythmbox if a slideshow starts"),
|
||||
true)
|
||||
-- Register
|
||||
dt.register_event("view-changed",playSlideshowMusic)
|
||||
|
||||
@@ -1,120 +1,119 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright 2014 by Tobias Jakobs.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
darktable video export script
|
||||
|
||||
ADDITIANAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* mencoder (MEncoder is from the MPlayer Team)
|
||||
* xdg-open
|
||||
* xdg-user-dir
|
||||
|
||||
WARNING
|
||||
This script is only testet with Linux
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,1})
|
||||
|
||||
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
||||
dt.print("Export Image "..tostring(number).."/"..tostring(total))
|
||||
end
|
||||
|
||||
local function checkIfBinExists(bin)
|
||||
local handle = io.popen("which "..bin)
|
||||
local result = handle:read()
|
||||
local ret
|
||||
handle:close()
|
||||
if (result) then
|
||||
--dt.print_error("true checkIfBinExists: "..bin)
|
||||
ret = true
|
||||
else
|
||||
dt.print_error(bin.." not found")
|
||||
ret = false
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
local function create_video_mencoder(storage, image_table, extra_data)
|
||||
if not checkIfBinExists("mencoder") then
|
||||
return
|
||||
end
|
||||
if not checkIfBinExists("xdg-open") then
|
||||
return
|
||||
end
|
||||
if not checkIfBinExists("xdg-user-dir") then
|
||||
return
|
||||
end
|
||||
|
||||
exportDirectory = dt.preferences.read("video_mencoder","ExportDirectory","string")
|
||||
exportFilename = "output.avi"
|
||||
framsePerSecond = dt.preferences.read("video_mencoder","FramesPerSecond","integer")
|
||||
|
||||
dt.print_error("Will try to create video now")
|
||||
-- Set the codec
|
||||
local codec = ""
|
||||
local codecPreferences = ""
|
||||
|
||||
codecPreferences = dt.preferences.read("video_mencoder","Codec","string")
|
||||
if (codecPreferences == "H.264 encoding") then
|
||||
codec = 'x264'
|
||||
end
|
||||
if (codecPreferences == "XviD encoding") then
|
||||
codec = 'xvid'
|
||||
end
|
||||
if not codecPreferences then
|
||||
codec = 'x264'
|
||||
end
|
||||
|
||||
-- Create the command
|
||||
local command = "mencoder -idx -nosound -noskip -ovc "..codec.." -lavcopts vcodec=mjpeg -o "..exportDirectory.."/"..exportFilename.." -mf fps="..framsePerSecond .." mf://"
|
||||
|
||||
for _,v in pairs(image_table) do
|
||||
command = command..v..","
|
||||
end
|
||||
|
||||
dt.print_error("this is the command: "..command)
|
||||
-- USE coroutine.yield. It does not block the UI
|
||||
coroutine.yield("RUN_COMMAND", command)
|
||||
|
||||
dt.print("Video created in "..exportDirectory)
|
||||
|
||||
if ( dt.preferences.read("video_mencoder","OpenVideo","bool") == true ) then
|
||||
-- USE coroutine.yield. It does not block the UI
|
||||
local playVideoCommand = "xdg-open "..exportDirectory.."/"..exportFilename
|
||||
coroutine.yield("RUN_COMMAND", playVideoCommand)
|
||||
end
|
||||
end
|
||||
|
||||
-- Preferences
|
||||
dt.preferences.register("video_mencoder", "FramesPerSecond", "float", "Video exort (MEncoder): Frames per second", "Frames per Second in the Video export", 15, 1, 99, 0.1 )
|
||||
dt.preferences.register("video_mencoder", "OpenVideo", "bool", "Video exort (MEncoder): Open video after export", "Opens the Video after the export with the standard video player", false )
|
||||
|
||||
local handle = io.popen("xdg-user-dir VIDEOS")
|
||||
local result = handle:read()
|
||||
handle:close()
|
||||
dt.preferences.register("video_mencoder", "ExportDirectory", "directory", "Video exort (MEncoder): Video export directory","A directory that will be used to export a Video",result)
|
||||
|
||||
-- Get the MEncoder codec list with: mencoder -ovc help
|
||||
dt.preferences.register("video_mencoder", "Codec", "enum", "Video exort (MEncoder): Codec","Video codec","H.264 encoding","H.264 encoding","XviD encoding")
|
||||
|
||||
-- Register
|
||||
dt.register_storage("video_mencoder", "Video Export (MEncoder)", show_status, create_video)
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright 2014 by Tobias Jakobs.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
darktable video export script
|
||||
|
||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* mencoder (MEncoder is from the MPlayer Team)
|
||||
* xdg-open
|
||||
* xdg-user-dir
|
||||
|
||||
WARNING
|
||||
This script is only tested with Linux
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{2,0,1},{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("video_mencoder",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("video_mencoder", msgid)
|
||||
end
|
||||
|
||||
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
||||
dt.print("Export Image "..tostring(number).."/"..tostring(total))
|
||||
end
|
||||
|
||||
local function create_video_mencoder(storage, image_table, extra_data)
|
||||
if not df.check_if_bin_exists("mencoder") then
|
||||
dt.print_error(_("mencoder not found"))
|
||||
return
|
||||
end
|
||||
if not df.check_if_bin_exists("xdg-open") then
|
||||
dt.print_error(_("xdg-open not found"))
|
||||
return
|
||||
end
|
||||
if not df.check_if_bin_exists("xdg-user-dir") then
|
||||
dt.print_error(_("xdg-user-dir not found"))
|
||||
return
|
||||
end
|
||||
|
||||
exportDirectory = dt.preferences.read("video_mencoder","ExportDirectory","string")
|
||||
exportFilename = "output.avi"
|
||||
framsePerSecond = dt.preferences.read("video_mencoder","FramesPerSecond","integer")
|
||||
|
||||
dt.print_error("Will try to create video now")
|
||||
-- Set the codec
|
||||
local codec = ""
|
||||
local codecPreferences = ""
|
||||
|
||||
codecPreferences = dt.preferences.read("video_mencoder","Codec","string")
|
||||
if (codecPreferences == "H.264 encoding") then
|
||||
codec = 'x264'
|
||||
end
|
||||
if (codecPreferences == "XviD encoding") then
|
||||
codec = 'xvid'
|
||||
end
|
||||
if not codecPreferences then
|
||||
codec = 'x264'
|
||||
end
|
||||
|
||||
-- Create the command
|
||||
local command = "mencoder -idx -nosound -noskip -ovc "..codec.." -lavcopts vcodec=mjpeg -o "..exportDirectory.."/"..exportFilename.." -mf fps="..framsePerSecond .." mf://"
|
||||
|
||||
for _,v in pairs(image_table) do
|
||||
command = command..v..","
|
||||
end
|
||||
|
||||
dt.print_error("this is the command: "..command)
|
||||
dt.control.execute( command)
|
||||
|
||||
dt.print("Video created in "..exportDirectory)
|
||||
|
||||
if ( dt.preferences.read("video_mencoder","OpenVideo","bool") == true ) then
|
||||
local playVideoCommand = "xdg-open "..exportDirectory.."/"..exportFilename
|
||||
dt.control.execute( playVideoCommand)
|
||||
end
|
||||
end
|
||||
|
||||
-- Preferences
|
||||
dt.preferences.register("video_mencoder", "FramesPerSecond", "float", "Video exort (MEncoder): Frames per second", "Frames per Second in the Video export", 15, 1, 99, 0.1 )
|
||||
dt.preferences.register("video_mencoder", "OpenVideo", "bool", "Video exort (MEncoder): Open video after export", "Opens the Video after the export with the standard video player", false )
|
||||
|
||||
local handle = io.popen("xdg-user-dir VIDEOS")
|
||||
local result = handle:read()
|
||||
handle:close()
|
||||
if (result == nil) then
|
||||
result = ""
|
||||
end
|
||||
dt.preferences.register("video_mencoder", "ExportDirectory", "directory", "Video exort (MEncoder): Video export directory","A directory that will be used to export a Video",result)
|
||||
|
||||
-- Get the MEncoder codec list with: mencoder -ovc help
|
||||
dt.preferences.register("video_mencoder", "Codec", "enum", "Video exort (MEncoder): Codec","Video codec","H.264 encoding","H.264 encoding","XviD encoding")
|
||||
|
||||
-- Register
|
||||
dt.register_storage("video_mencoder", "Video Export (MEncoder)", show_status, create_video)
|
||||
|
||||
20
examples/de_DE/LC_MESSAGES/gettextExample.po
Normal file
20
examples/de_DE/LC_MESSAGES/gettextExample.po
Normal file
@@ -0,0 +1,20 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Lua gettextTest\n"
|
||||
"POT-Creation-Date: 2016-01-07 20:33+0100\n"
|
||||
"PO-Revision-Date: 2016-01-07 21:32+0100\n"
|
||||
"Last-Translator: Tobias Jakobs <tobias.jakobs@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.8.4\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Poedit-KeywordsList: gettext;dgettext:2;dcgettext:2;ngettext:1,2;dngettext:2,3\n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
|
||||
#: gettextExample.lua:36
|
||||
msgid "Hello World!"
|
||||
msgstr "Hallo Welt!"
|
||||
76
examples/gettextExample.lua
Normal file
76
examples/gettextExample.lua
Normal file
@@ -0,0 +1,76 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright 2016 by Tobias Jakobs.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
darktable script to show how translations works
|
||||
|
||||
To create the .po file run:
|
||||
xgettext -l lua gettextExample.lua
|
||||
|
||||
xgettext is not a lua tool, it knows (almost) nothing about Lua, and not
|
||||
enough to do a proper parsing. It takes a text file (In our case a Lua
|
||||
file) and recognises a few (language dependant) keyword in there.
|
||||
It matches those keywords with internal description on how functions are
|
||||
called and creates the .po file accordingly. (For example, it knows that
|
||||
the first argument of gettext() is the translated string, but that it's
|
||||
the second argument for dgettext)
|
||||
This is important because it means that if you use some neat Lua tricks
|
||||
(like renaming functions) xgettext won't recognize those calls and won't
|
||||
extract the string to the .po file.
|
||||
So, this is why we create a local variagle gettext = dt.gettext, so
|
||||
xgettext recognises gettext.gettext as a function but not dt.gettext.gettext
|
||||
|
||||
To create a .mo file run:
|
||||
msgfmt -v gettextExample.po -o gettextExample.mo
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file (Add 'require "gettextExample"' to luarc.)
|
||||
* copy the script to: .config/darktable/lua/
|
||||
* copy the gettextExample.mo to .config/darktable/lua/de_DE/LC_MESSAGES
|
||||
|
||||
You need to start darktable with the Lua debug option: darktable -d lua
|
||||
$LANG must set to: de_DE
|
||||
|
||||
The script run on darktable startup and should output this three lines:
|
||||
|
||||
LUA ERROR Hello World!
|
||||
LUA ERROR Bild
|
||||
LUA ERROR Hallo Welt!
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0}))
|
||||
|
||||
-- Not translated Text
|
||||
dt.print_error("Hello World!")
|
||||
|
||||
local gettext = dt.gettext
|
||||
-- Translate a string using the darktable textdomain
|
||||
dt.print_error(gettext.gettext("image"))
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
|
||||
gettext.bindtextdomain("gettextExample",dt.configuration.config_dir.."/lua/locale/")
|
||||
-- Translate a string using the specified textdomain
|
||||
dt.print_error(gettext.dgettext("gettextExample", 'Hello World!'))
|
||||
|
||||
-- Define a local function called _ to make the code more readable and have it call dgettext
|
||||
-- with the proper domain.
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("gettextExample", msgid)
|
||||
end
|
||||
dt.print_error(_('Hello World!'))
|
||||
@@ -27,7 +27,7 @@ USAGE
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,0})
|
||||
dt.configuration.check_version(...,{2,0,0},{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
dt.print("hello, world")
|
||||
|
||||
138
examples/moduleExample.lua
Normal file
138
examples/moduleExample.lua
Normal file
@@ -0,0 +1,138 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2016 Tobias Jakobs
|
||||
|
||||
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/>.
|
||||
]]
|
||||
--[[
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
To do this add this line to the file .config/darktable/luarc:
|
||||
require "moduleExample"
|
||||
|
||||
* it creates a new example lighttable module
|
||||
|
||||
More informations about building user interface elements:
|
||||
https://www.darktable.org/usermanual/ch09.html.php#lua_gui_example
|
||||
And about new_widget here:
|
||||
https://www.darktable.org/lua-api/index.html.php#darktable_new_widget
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- add a new lib
|
||||
local check_button = dt.new_widget("check_button"){label = "MyCheck_button", value = true}
|
||||
local combobox = dt.new_widget("combobox"){label = "MyCombobox", value = 2, "8", "16", "32"}
|
||||
|
||||
--https://www.darktable.org/lua-api/ar01s02s54.html.php
|
||||
local entry = dt.new_widget("entry")
|
||||
{
|
||||
text = "test",
|
||||
placeholder = "placeholder",
|
||||
is_password = false,
|
||||
editable = true,
|
||||
tooltip = "Tooltip Text",
|
||||
reset_callback = function(self) self.text = "text" end
|
||||
}
|
||||
|
||||
local file_chooser_button = dt.new_widget("file_chooser_button")
|
||||
{
|
||||
title = "MyFile_chooser_button", -- The title of the window when choosing a file
|
||||
value = "", -- The currently selected file
|
||||
is_directory = false -- True if the file chooser button only allows directories to be selecte
|
||||
}
|
||||
|
||||
local label = dt.new_widget("label")
|
||||
label.label = "MyLabel" -- This is an alternative way to the "{}" syntax to set a property
|
||||
|
||||
local separator = dt.new_widget("separator"){}
|
||||
local slider = dt.new_widget("slider")
|
||||
{
|
||||
label = "MySlider",
|
||||
soft_min = 10, -- The soft minimum value for the slider, the slider can't go beyond this point
|
||||
soft_max = 100, -- The soft maximum value for the slider, the slider can't go beyond this point
|
||||
hard_min = 0, -- The hard minimum value for the slider, the user can't manually enter a value beyond this point
|
||||
hard_max = 1000, -- The hard maximum value for the slider, the user can't manually enter a value beyond this point
|
||||
value = 52 -- The current value of the slider
|
||||
}
|
||||
|
||||
if (dt.configuration.api_version_major >= 6) then
|
||||
local section_label = dt.new_widget("section_label")
|
||||
{
|
||||
label = "MySectionLabel"
|
||||
}
|
||||
|
||||
dt.register_lib(
|
||||
"exampleModule", -- Module name
|
||||
"exampleModule", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = "MyButton",
|
||||
clicked_callback = function (_)
|
||||
dt.print("Button clicked")
|
||||
end
|
||||
},
|
||||
check_button,
|
||||
combobox,
|
||||
entry,
|
||||
file_chooser_button,
|
||||
label,
|
||||
separator,
|
||||
slider,
|
||||
section_label
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
else
|
||||
dt.register_lib(
|
||||
"exampleModule", -- Module name
|
||||
"exampleModule", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = "MyButton",
|
||||
clicked_callback = function (_)
|
||||
dt.print("Button clicked")
|
||||
end
|
||||
},
|
||||
check_button,
|
||||
combobox,
|
||||
entry,
|
||||
file_chooser_button,
|
||||
label,
|
||||
separator,
|
||||
slider
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
end
|
||||
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
-- kate: hl Lua;
|
||||
@@ -16,13 +16,13 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
darktable script to show the different prefference types that are possible with the Lua
|
||||
darktable script to show the different preference types that are possible with Lua
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,1})
|
||||
dt.configuration.check_version(...,{2,0,1},{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
||||
"preferenceExamplesString", -- name
|
||||
40
examples/printExamples.lua
Normal file
40
examples/printExamples.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2017 Tobias Jakobs
|
||||
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/>.
|
||||
]]
|
||||
|
||||
--[[
|
||||
Print Examples
|
||||
prints "hello world when DT starts
|
||||
USAGE
|
||||
* require this file from your main lua config file:
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{5,0,0})
|
||||
|
||||
-- Will print a string to the darktable control log (the long
|
||||
-- overlayed window that appears over the main panel).
|
||||
dt.print("print")
|
||||
|
||||
-- This function will print its parameter if the Lua logdomain is
|
||||
-- activated. Start darktable with the "-d lua" command line option
|
||||
-- to enable the Lua logdomain.
|
||||
dt.print_error("print error")
|
||||
|
||||
-- This function will print its parameter if the Lua logdomain is
|
||||
-- activated. Start darktable with the "-d lua" command line option
|
||||
-- to enable the Lua logdomain.
|
||||
dt.print_log("print log")
|
||||
|
||||
--
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
34
examples/running_os.lua
Normal file
34
examples/running_os.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2017 Tobias Jakobs
|
||||
|
||||
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/>.
|
||||
]]
|
||||
--[[
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
To do this add this line to the file .config/darktable/luarc:
|
||||
require "running_os"
|
||||
|
||||
prints the operating system
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{5,0,0})
|
||||
|
||||
dt.print("You are running: "..dt.configuration.running_os)
|
||||
|
||||
--
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
@@ -33,7 +33,7 @@ Note that you need to restart DT for your changes to enabled scripts to take eff
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
local io = require "io"
|
||||
dt.configuration.check_version(...,{2,0,0})
|
||||
dt.configuration.check_version(...,{3,0,0})
|
||||
|
||||
local output = io.popen("cd "..dt.configuration.config_dir.."/lua ;find . -name \\*.lua -print")
|
||||
local my_name={...}
|
||||
|
||||
218
lib/dtutils.lua
Normal file
218
lib/dtutils.lua
Normal file
@@ -0,0 +1,218 @@
|
||||
local dtutils = {}
|
||||
|
||||
dtutils.libdoc = {
|
||||
Name = [[dtutils]],
|
||||
Synopsis = [[A Darktable lua utilities library]],
|
||||
Usage = [[local du = require "lib/dtutils"]],
|
||||
Description = [[dtutils provides a common library of functions used to build
|
||||
lua scripts. There are also sublibraries that provide more functions.]],
|
||||
Return_Value = [[du - library - the library of functions]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[dtutils.debug(3), dtutils.file(3), dtutils.log(3), dtutils.string(3)]],
|
||||
Reference = [[]],
|
||||
License = [[This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.]],
|
||||
Copyright = [[Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
|
||||
Copyright (C) 2016 Tobias Jakobs]],
|
||||
functions = {}
|
||||
}
|
||||
|
||||
local dt = require "darktable"
|
||||
|
||||
local log = require "lib/dtutils.log"
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
dtutils.libdoc.functions["split"] = {
|
||||
Name = [[split]],
|
||||
Synopsis = [[split a string on a specified separator]],
|
||||
Usage = [[local du = require "lib/dtutils"
|
||||
|
||||
local result = du.split(str, pat)
|
||||
str - string - the string to split
|
||||
pat - string - the pattern to split on]],
|
||||
Description = [[split separates a string into a table of strings. The strings are separated at each
|
||||
occurrence of the supplied pattern. The pattern may be any pattern as described in the lua docs.
|
||||
Each match of the pattern is consumed and not returned.]],
|
||||
Return_Value = [[result - a table of strings on success, or an empty table on error]],
|
||||
Limitations = [[]],
|
||||
Example = [[split("/a/long/path/name/to/a/file.txt", "/") would return a table like
|
||||
{a, "long", "path", "name", "to", a, "file.txt"}]],
|
||||
See_Also = [[]],
|
||||
Reference = [[http://lua-users.org/wiki/SplitJoin]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils.split(str, pat)
|
||||
local t = {}
|
||||
local fpat = "(.-)" .. pat
|
||||
local last_end = 1
|
||||
local s, e, cap = str:find(fpat, 1)
|
||||
while s do
|
||||
if s ~= 1 or cap ~= "" then
|
||||
table.insert(t,cap)
|
||||
end
|
||||
last_end = e+1
|
||||
s, e, cap = str:find(fpat, last_end)
|
||||
end
|
||||
if last_end <= #str then
|
||||
cap = str:sub(last_end)
|
||||
table.insert(t, cap)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
dtutils.libdoc.functions["join"] = {
|
||||
Name = [[join]],
|
||||
Synopsis = [[join a table of strings with a specified separator]],
|
||||
Usage = [[local du = require "lib/dtutils"
|
||||
|
||||
local result = du.join(tabl, pat)
|
||||
tabl - a table of strings
|
||||
pat - a separator]],
|
||||
Description = [[join assembles a table of strings into a string with the specified pattern
|
||||
in between each string]],
|
||||
Return_Value = [[result - string - the joined string on success, or an empty string on failure]],
|
||||
Limitations = [[]],
|
||||
Example = [[join({a, "long", "path", "name", "to", a, "file.txt"}, " ") would return the string
|
||||
"a long path name to a file.txt"]],
|
||||
See_Also = [[]],
|
||||
Reference = [[http://lua-users.org/wiki/SplitJoin]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils.join(tabl, pat)
|
||||
returnstr = ""
|
||||
for i,str in pairs(tabl) do
|
||||
returnstr = returnstr .. str .. pat
|
||||
end
|
||||
return string.sub(returnstr, 1, -(pat:len() + 1))
|
||||
end
|
||||
|
||||
--[[
|
||||
NAME
|
||||
prequire - a protected lua require
|
||||
|
||||
SYNOPSIS
|
||||
local du = require "lib/dtutils"
|
||||
|
||||
local result = du.prequire(req_name)
|
||||
req_name - the filename of the lua code to load without the ".lua" filetype
|
||||
|
||||
DESCRIPTION
|
||||
prequire is a protected require that can survive an error in the code being loaded without
|
||||
bringing down the calling routine.
|
||||
|
||||
RETURN VALUE
|
||||
result - the code or true on success, otherwise an error message
|
||||
|
||||
EXAMPLE
|
||||
prequire("lib/dtutils.file") which would load lib/dtutils/file.lua
|
||||
|
||||
]]
|
||||
|
||||
dtutils.libdoc.functions["prequire"] = {
|
||||
Name = [[prequire]],
|
||||
Synopsis = [[a protected lua require]],
|
||||
Usage = [[local du = require "lib/dtutils"
|
||||
|
||||
local status, lib = du.prequire(req_name)
|
||||
req_name - the filename of the lua code to load without the ".lua" filetype]],
|
||||
Description = [[prequire is a protected require that can survive an error in the code being loaded without
|
||||
bringing down the calling routine.]],
|
||||
Return_Value = [[status - boolean - true on success
|
||||
lib - if status is true, then the code, otherwise an error message]],
|
||||
Limitations = [[]],
|
||||
Example = [[local status, lib = prequire("lib/dtutils.file") which would load lib/dtutils/file.lua which
|
||||
would return a status of true and the reference to the library in lib.]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils.prequire(req_name)
|
||||
local status, lib = pcall(require, req_name)
|
||||
if status then
|
||||
log.msg(log.info, "Loaded " .. req_name)
|
||||
else
|
||||
log.msg(log.info, "Error loading " .. req_name)
|
||||
end
|
||||
return status, lib
|
||||
end
|
||||
|
||||
dtutils.libdoc.functions["spairs"] = {
|
||||
Name = [[spairs]],
|
||||
Synopsis = [[an iterator that provides sorted pairs from a table]],
|
||||
Usage = [[local du = require "lib/dtutils"
|
||||
|
||||
for key, value in du.spairs(t, order) do
|
||||
t - table - table of key, value pairs
|
||||
order - function - an optional function to sort the pairs
|
||||
if none is supplied, table.sort() is used]],
|
||||
Description = [[spairs is an iterator that returns key, value pairs from a table in sorted
|
||||
order. The sorting order is the result of table.sort() if no function is
|
||||
supplied, otherwise sorting is done as specified in the function.]],
|
||||
Return_Value = [[]],
|
||||
Limitations = [[]],
|
||||
Example = [[HighScore = { Robin = 8, Jon = 10, Max = 11 }
|
||||
|
||||
-- basic usage, just sort by the keys
|
||||
for k,v in spairs(HighScore) do
|
||||
print(k,v)
|
||||
end
|
||||
--> Jon 10
|
||||
--> Max 11
|
||||
--> Robin 8
|
||||
|
||||
-- this uses an custom sorting function ordering by score descending
|
||||
for k,v in spairs(HighScore, function(t,a,b) return t[b] < t[a] end) do
|
||||
print(k,v)
|
||||
end
|
||||
--> Max 11
|
||||
--> Jon 10
|
||||
--> Robin 8]],
|
||||
See_Also = [[]],
|
||||
Reference = [[Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
-- Sort a table
|
||||
function dtutils.spairs(_table, order) -- Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
|
||||
-- collect the keys
|
||||
local keys = {}
|
||||
for _key in pairs(_table) do keys[#keys + 1] = _key end
|
||||
|
||||
-- if order function given, sort by it by passing the table and keys a, b,
|
||||
-- otherwise just sort the keys
|
||||
if order then
|
||||
table.sort(keys, function(a,b) return order(_table, a, b) end)
|
||||
else
|
||||
table.sort(keys)
|
||||
end
|
||||
|
||||
-- return the iterator function
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if keys[i] then
|
||||
return keys[i], _table[keys[i]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return dtutils
|
||||
161
lib/dtutils/debug.lua
Normal file
161
lib/dtutils/debug.lua
Normal file
@@ -0,0 +1,161 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright (c) 2014 Jérémy Rosen
|
||||
Copyright (c) 2016 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/>.
|
||||
]]
|
||||
--[[
|
||||
DEBUG HELPERS
|
||||
A collection of helper functions to help debugging lua scripts.
|
||||
|
||||
require it as
|
||||
|
||||
dhelpers = require "lib/dtutils.debug"
|
||||
|
||||
Each function is documented in its own header
|
||||
|
||||
|
||||
]]
|
||||
|
||||
|
||||
local dt = require "darktable"
|
||||
local io = require "io"
|
||||
local table = require "table"
|
||||
require "darktable.debug"
|
||||
local log = require "lib/dtutils.log"
|
||||
local M = {} -- The actual content of the module
|
||||
|
||||
M.libdoc = {
|
||||
Name = [[dtutils.debug]],
|
||||
Synopsis = [[debugging helpers used in developing darktable lua scripts]],
|
||||
Usage = [[local dd = require "lib/dtutils.debug"]],
|
||||
Description = [[dtutils.debug provides an interface to the darktable debugging routines.]],
|
||||
Return_Value = [[dd - library - the darktable lua debugging helpers]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = log.libdoc.License,
|
||||
Copyright = [[Copyright (c) 2014 Jérémy Rosen
|
||||
Copyright (c) 2016 Bill Ferguson
|
||||
]],
|
||||
functions = {}
|
||||
}
|
||||
|
||||
M.libdoc.functions["tracepoint"] = {
|
||||
Name = [[tracepoint]],
|
||||
Synopsis = [[print out a tracepoint and dump the arguments]],
|
||||
Usage = [[local dd = require "lib/dtutils.debug"
|
||||
|
||||
local result = tracepoint(name, ...)
|
||||
name - string - the name of the tracepoint to print out
|
||||
... - arguments - variables to dump the contents of]],
|
||||
Description = [[tracepoint prints its name and dumps its parameters using
|
||||
dt.debug]],
|
||||
Return_Value = [[result - ... - the supplied argument list]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function M.tracepoint(name,...)
|
||||
log.msg(log.always, 4, "***** "..name.." ****")
|
||||
params = {...}
|
||||
log.msg(log.always, 0, dt.debug.dump(params,"parameters"))
|
||||
return ...;
|
||||
end
|
||||
|
||||
M.libdoc.functions["new_tracepoint"] = {
|
||||
Name = [[new_tracepoint]],
|
||||
Synopsis = [[create a function returning a tracepoint]],
|
||||
Usage = [[local dd = require "lib/dtutils.debug"
|
||||
|
||||
local result = new_tracepoint(name, ...)
|
||||
name - string - the name of the tracepoint to print out
|
||||
... - arguments - variables to dump the contents of]],
|
||||
Description = [[A function that returns a tracepoint function with the given name
|
||||
This is mainly used to debug callbacks.]],
|
||||
Return_Value = [[result - function - a function that returns the result of a tracepoint]],
|
||||
Limitations = [[]],
|
||||
Example = [[register_event(event, dd.new_tracepoint("hit callback"))
|
||||
|
||||
will print the following each time the callback is called
|
||||
|
||||
**** hit callback ****
|
||||
<all the callback's parameters dumped>]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function M.new_tracepoint(name)
|
||||
return function(...) return M.tracepoint(name,...) end
|
||||
end
|
||||
|
||||
|
||||
M.libdoc.functions["dprint"] = {
|
||||
Name = [[dprint]],
|
||||
Synopsis = [[pass a variable to dt.debug.dump and print the results to stdout]],
|
||||
Usage = [[local dd = require "lib/dtutils.debug"
|
||||
|
||||
dd.dprint(var)
|
||||
var - variable - any variable that you want to see the contents of]],
|
||||
Description = [[Wrapper around debug.dump, will directly print to stdout,
|
||||
same calling convention]],
|
||||
Return_Value = [[]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function M.dprint(...)
|
||||
log.msg(log.always, 4, dt.debug.dump(...))
|
||||
end
|
||||
|
||||
M.libdoc.functions["terse_dump"] = {
|
||||
Name = [[terse_dump]],
|
||||
Synopsis = [[set dt.debug.known to shorten all image dumps to a single line]],
|
||||
Usage = [[local dd = require "lib/dtutils.debug"
|
||||
|
||||
dd.terse_dump()]],
|
||||
Description = [[terse_dump sets dt.debug.known to shorten all images to a single line.
|
||||
If you don't need to debug the content of images, this will avoid them flooding your logs]],
|
||||
Return_Value = [[]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function M.terse_dump()
|
||||
for _,v in ipairs(dt.database) do
|
||||
dt.debug.known[v] = tostring(v)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
return M
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
|
||||
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;
|
||||
394
lib/dtutils/file.lua
Normal file
394
lib/dtutils/file.lua
Normal file
@@ -0,0 +1,394 @@
|
||||
local dtutils_file = {}
|
||||
local dt = require "darktable"
|
||||
|
||||
local log = require "lib/dtutils.log"
|
||||
|
||||
dtutils_file.libdoc = {
|
||||
Name = [[dtutils.file]],
|
||||
Synopsis = [[common darktable lua file functions]],
|
||||
Usage = [[local df = require "lib/dtutils.file"]],
|
||||
Description = [[[dtutils.file provides common file manipulation functions used in
|
||||
constructing Darktable lua scripts]],
|
||||
Return_Value = [[df - library - the file functions]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = log.libdoc.License,
|
||||
Copyright = [[Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.]],
|
||||
Copyright = [[Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
|
||||
Copyright (C) 2016 Tobias Jakobs]],
|
||||
functions = {}
|
||||
}
|
||||
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("dtutils.file",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("dtutils.file", msgid)
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["check_if_bin_exists"] = {
|
||||
Name = [[check_if_bin_exists]],
|
||||
Synopsis = [[check if an executable is in the path]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.check_if_bin_exists(bin)
|
||||
bin - string - the binary to check for]],
|
||||
Description = [[check_if_bin_exists checks to see if the specified binary executable is
|
||||
in the path.]],
|
||||
Return_Value = [[result - boolean - true if the executable was found, false if not]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.check_if_bin_exists(bin)
|
||||
local result = os.execute("which " .. bin)
|
||||
if not result then
|
||||
result = false
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["split_filepath"] = {
|
||||
Name = [[split_filepath]],
|
||||
Synopsis = [[split a filepath into parts]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.split_filepath(filepath)
|
||||
filepath - string - path and filename]],
|
||||
Description = [[split_filepath splits a filepath into the path, filename, basename and filetype and puts
|
||||
that in a table]],
|
||||
Return_Value = [[result - table - a table containing the path, filename, basename, and filetype]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.split_filepath(str)
|
||||
-- strip out single quotes from quoted pathnames
|
||||
str = string.gsub(str, "'", "")
|
||||
local result = {}
|
||||
-- Thank you Tobias Jakobs for the awesome regular expression, which I tweaked a little
|
||||
result["path"], result["filename"], result["basename"], result["filetype"] = string.match(str, "(.-)(([^\\/]-)%.?([^%.\\/]*))$")
|
||||
return result
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["get_path"] = {
|
||||
Name = [[get_path]],
|
||||
Synopsis = [[get the path from a file path]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.get_path(filepath)
|
||||
filepath - string - path and filename]],
|
||||
Description = [[get_path strips the filename and filetype from a path and returns the path]],
|
||||
Return_Value = [[result - string - the path]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.get_path(str)
|
||||
local parts = dtutils_file.split_filepath(str)
|
||||
return parts["path"]
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["get_filename"] = {
|
||||
Name = [[get_filename]],
|
||||
Synopsis = [[get the filename and extension from a file path]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.get_filename(filepath)
|
||||
filepath - string - path and filename]],
|
||||
Description = [[get_filename strips the path from a filepath and returns the filename]],
|
||||
Return_Value = [[result - string - the file name and type]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.get_filename(str)
|
||||
local parts = dtutils_file.split_filepath(str)
|
||||
return parts["filename"]
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["get_basename"] = {
|
||||
Name = [[get_basename]],
|
||||
Synopsis = [[get the filename without the path or extension]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.get_basename(filepath)
|
||||
filepath - string - path and filename]],
|
||||
Description = [[get_basename returns the name of the file without the path or filetype
|
||||
]],
|
||||
Return_Value = [[result - string - the basename of the file]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.get_basename(str)
|
||||
local parts = dtutils_file.split_filepath(str)
|
||||
return parts["basename"]
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["get_filetype"] = {
|
||||
Name = [[get_filetype]],
|
||||
Synopsis = [[get the filetype from a filename]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.get_filetype(filepath)
|
||||
filepath - string - path and filename]],
|
||||
Description = [[get_filetype returns the filetype from the supplied filepath]],
|
||||
Return_Value = [[result - string - the filetype]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.get_filetype(str)
|
||||
local parts = dtutils_file.split_filepath(str)
|
||||
return parts["filetype"]
|
||||
end
|
||||
|
||||
|
||||
-- Thanks Tobias Jakobs for the idea
|
||||
dtutils_file.libdoc.functions["check_if_file_exists"] = {
|
||||
Name = [[check_if_file_exists]],
|
||||
Synopsis = [[check if a file or path exist]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.check_if_file_exists(filepath)
|
||||
filepath - string - a file or path to check]],
|
||||
Description = [[check_if_file_exists checks to see if a file or path exists]],
|
||||
Return_Value = [[result - boolean - true if the file or path exists, false if it doesn't]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.check_if_file_exists(filepath)
|
||||
local result = os.execute("test -e " .. filepath)
|
||||
if not result then
|
||||
result = false
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["chop_filetype"] = {
|
||||
Name = [[chop_filetype]],
|
||||
Synopsis = [[remove a filetype from a filename]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.chop_filetype(path)
|
||||
path - string - a filename with or without a path]],
|
||||
Description = [[chop_filetype removes the filetype from the filename]],
|
||||
Return_Value = [[result - string - the path and filename without the filetype]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.chop_filetype(path)
|
||||
local length = dtutils_file.get_filetype(path):len() + 2
|
||||
return string.sub(path, 1, -length)
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["file_copy"] = {
|
||||
Name = [[file_copy]],
|
||||
Synopsis = [[copy a file to another name/location]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.file_copy(fromFile, toFile)
|
||||
fromFile - string - name of file to copy from
|
||||
toFile - string - name of file to copy to]],
|
||||
Description = [[copy a file using a succession of methods from operating system
|
||||
to a pure lua solution]],
|
||||
Return_Value = [[result - boolean - nil on error, true on success]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.file_copy(fromFile, toFile)
|
||||
local result = nil
|
||||
-- if cp exists, use it
|
||||
if dtutils_file.check_if_bin_exists("cp") then
|
||||
result = os.execute("cp '" .. fromFile .. "' '" .. toFile .. "'")
|
||||
end
|
||||
-- if cp was not present, or if cp failed, then a pure lua solution
|
||||
if not result then
|
||||
local fileIn, err = io.open(fromFile, 'rb')
|
||||
if fileIn then
|
||||
local fileOut, errr = io.open(toFile, 'w')
|
||||
if fileOut then
|
||||
local content = fileIn:read(4096)
|
||||
while content do
|
||||
fileOut:write(content)
|
||||
content = fileIn:read(4096)
|
||||
end
|
||||
result = true
|
||||
fileIn:close()
|
||||
fileOut:close()
|
||||
else
|
||||
log.msg(log.error, errr)
|
||||
end
|
||||
else
|
||||
log.msg(log.error, err)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["file_move"] = {
|
||||
Name = [[file_move]],
|
||||
Synopsis = [[move a file from one directory to another]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.file_move(fromFile, toFile)
|
||||
fromFile - string - name of the original file
|
||||
toFile - string - the new file location and name]],
|
||||
Description = [[Move a file from one place to another. Try a succession of methods from
|
||||
builtin to operating system to a pure lua solution.]],
|
||||
Return_Value = [[result - boolean - nil on error, some value on success]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.file_move(fromFile, toFile)
|
||||
local success = os.rename(fromFile, toFile)
|
||||
if not success then
|
||||
-- an error occurred, so let's try using the operating system function
|
||||
if dtutils_file.check_if_bin_exists("mv") then
|
||||
success = os.execute("mv '" .. fromFile .. "' '" .. toFile .. "'")
|
||||
end
|
||||
-- if the mv didn't exist or succeed, then...
|
||||
if not success then
|
||||
-- pure lua solution
|
||||
success = dtutils_file.file_copy(fromFile, toFile)
|
||||
if success then
|
||||
os.remove(fromFile)
|
||||
else
|
||||
log.msg(log.error, "Unable to move " .. fromFile .. " to " .. toFile .. ". Leaving " .. fromFile .. " in place.")
|
||||
end
|
||||
end
|
||||
end
|
||||
return success -- nil on error, some value if success
|
||||
end
|
||||
|
||||
dtutils_file.libdoc.functions["filename_increment"] = {
|
||||
Name = [[filename_increment]],
|
||||
Synopsis = [[add a two digit increment to a filename]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.filename_increment(filepath)
|
||||
filepath - string - filename to increment]],
|
||||
Description = [[filename_increment solves the problem of filename confllict by adding an
|
||||
increment to the filename. If the supplied filename has no increment then
|
||||
"01" is added to the basename. If the filename already has an increment, then
|
||||
1 is added to it and the filename returned.]],
|
||||
Return_Value = [[result - string - the incremented filename]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.filename_increment(filepath)
|
||||
|
||||
-- break up the filepath into parts
|
||||
local path = dtutils_file.get_path(filepath)
|
||||
local basename = dtutils_file.get_basename(filepath)
|
||||
local filetype = dtutils_file.get_filetype(filepath)
|
||||
|
||||
-- check to see if we've incremented before
|
||||
local increment = string.match(basename, "_(%d-)$")
|
||||
|
||||
if increment then
|
||||
-- we do 2 digit increments so make sure we didn't grab part of the filename
|
||||
if string.len(increment) > 2 then
|
||||
-- we got the filename so set the increment to 01
|
||||
increment = "01"
|
||||
else
|
||||
increment = string.format("%02d", tonumber(increment) + 1)
|
||||
basename = string.gsub(basename, "_(%d-)$", "")
|
||||
end
|
||||
else
|
||||
increment = "01"
|
||||
end
|
||||
local incremented_filepath = path .. basename .. "_" .. increment .. "." .. filetype
|
||||
|
||||
return incremented_filepath
|
||||
end
|
||||
|
||||
|
||||
dtutils_file.libdoc.functions["create_unique_filename"] = {
|
||||
Name = [[create_unique_filename]],
|
||||
Synopsis = [[create a unique filename from the supplied argment]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.create_unique_filename(filepath)
|
||||
filepath - string - the path and filename requested]],
|
||||
Description = [[create_unique_filename takes a requested filepath and checks to see if
|
||||
it exists. If if doesn't then it's returned intact. If it already exists, then a two
|
||||
digit increment is added to the filename and it is tested again. The increment keeps
|
||||
increasing until either a unique filename is found or there have been 100 attempts.]],
|
||||
Return_Value = [[result - string - the incremented filename]],
|
||||
Limitations = [[create_unique_filename will only attempt 100 increments.]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.create_unique_filename(filepath)
|
||||
while dtutils_file.check_if_file_exists(filepath) do
|
||||
filepath = dtutils_file.filename_increment(filepath)
|
||||
-- limit to 99 more exports of the original export
|
||||
if string.match(dtfileutils.get_basename(filepath), "_(d-)$") == "99" then
|
||||
break
|
||||
end
|
||||
end
|
||||
return filepath
|
||||
end
|
||||
|
||||
return dtutils_file
|
||||
291
lib/dtutils/log.lua
Normal file
291
lib/dtutils/log.lua
Normal file
@@ -0,0 +1,291 @@
|
||||
|
||||
local dtutils_log = {}
|
||||
|
||||
dtutils_log.libdoc = {
|
||||
Name = [[dtutils.log]],
|
||||
Synopsis = [[darktable lua logging library]],
|
||||
Usage = [[local log = require "lib/dtutils.log"]],
|
||||
Description = [[log provides a multi-level logging solution for use with
|
||||
the darktable lua scripts. With this library you can leave log messages
|
||||
scattered through out your code and only turn them on as necessary.]],
|
||||
Return_Value = [[log - library - the darktable lua logging functions]],
|
||||
Limitations = [[]],
|
||||
Example = [[local log = require "lib/dtutils.log"
|
||||
|
||||
local cur_level = log.log_level()
|
||||
log.log_level(log.warn)
|
||||
|
||||
print out warning, error and success messages as code is running
|
||||
|
||||
log.log_level(debug)
|
||||
|
||||
print out debugging messages too because this isnt working
|
||||
|
||||
log.log_level(info)
|
||||
|
||||
I want to make sure this is working ok
|
||||
|
||||
log.log_level(cur_level)
|
||||
|
||||
reset the logging level back to normal]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.]],
|
||||
Copyright = [[Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.]],
|
||||
functions = {}
|
||||
}
|
||||
|
||||
local dt = require "darktable"
|
||||
local dt_print_error = dt.print_error
|
||||
-- work around until dt.print_log()
|
||||
local dt_print_log = dt.print_error
|
||||
local dt_print = dt.print
|
||||
|
||||
-- set the default log levels
|
||||
|
||||
dtutils_log.debug = {
|
||||
label = "DEBUG:",
|
||||
enabled = false,
|
||||
engine = dt_print_log,
|
||||
level = 1,
|
||||
}
|
||||
dtutils_log.info = {
|
||||
label = "INFO:",
|
||||
enable = false,
|
||||
engine = dt_print_log,
|
||||
level = 2,
|
||||
}
|
||||
dtutils_log.warn = {
|
||||
label = "WARN:",
|
||||
enabled = false,
|
||||
engine = dt_print_log,
|
||||
level = 3,
|
||||
}
|
||||
dtutils_log.error = {
|
||||
label = "ERROR:",
|
||||
enabled = true,
|
||||
engine = dt_print_error,
|
||||
level = 4,
|
||||
}
|
||||
dtutils_log.success = {
|
||||
label = "SUCCESS:",
|
||||
enabled = true,
|
||||
engine = dt_print_log,
|
||||
level = 5,
|
||||
}
|
||||
dtutils_log.screen = {
|
||||
label = "",
|
||||
enabled = true,
|
||||
engine = dt_print,
|
||||
level = 9,
|
||||
}
|
||||
dtutils_log.always = {
|
||||
label = "",
|
||||
enabled = true,
|
||||
engine = dt_print_log,
|
||||
level = 9,
|
||||
}
|
||||
dtutils_log.critical = {
|
||||
label = "CRITICAL:",
|
||||
enabled = true,
|
||||
engine = print,
|
||||
level = 9,
|
||||
}
|
||||
|
||||
dtutils_log.libdoc.functions["caller"] = {
|
||||
Name = [[caller]],
|
||||
Synopsis = [[get the name and line number of the calling routine]],
|
||||
Usage = [[local log = require "lib/dtutils.log"
|
||||
|
||||
result = log.caller(level)
|
||||
level - number - the number of stack levels to go down to retrieve the caller routine information]],
|
||||
Description = [[caller gets the name and line number of the calling routine and returns it]],
|
||||
Return_Value = [[result - string - the name and line number of the calling function or 'callback: ' if the attempt to get the
|
||||
caller returns nil]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_log.caller(level)
|
||||
local name = debug.getinfo(level).name
|
||||
local lineno = nil
|
||||
local source = nil
|
||||
if name then
|
||||
lineno = debug.getinfo(level).currentline
|
||||
-- returns the path to the file prefixed with @
|
||||
source = debug.getinfo(level).source
|
||||
-- we just need the filename, so grab it from the string
|
||||
-- Thanks, Tobias Jakobs :-)
|
||||
source = string.match(source, "@.-([^\\/]-%.?[^%.\\/]*)$")
|
||||
return name .. ": " .. source .. ": " .. lineno .. ":"
|
||||
else
|
||||
return "callback:"
|
||||
end
|
||||
end
|
||||
|
||||
dtutils_log.libdoc.functions["msg"] = {
|
||||
Name = [[msg]],
|
||||
Synopsis = [[print a log message]],
|
||||
Usage = [[local log = require "lib/log"
|
||||
|
||||
log.msg(level, ...)
|
||||
level - table - the type of message, one of:
|
||||
log.debug - debugging messages
|
||||
log.info - informational messages
|
||||
log.warn - warning messages
|
||||
log.error - error messages
|
||||
log.success - success messages
|
||||
log.always - an internal message for debugging
|
||||
log.screen - output 1 line of text to the screen
|
||||
log.critical - print a critical message to the console
|
||||
|
||||
... - string(s) - the message to print, which could be a comma separated set of strings]],
|
||||
Description = [[msg checks the level to see if it is enabled, then prints the level type and message if it is.
|
||||
Messages are output using the engine configured in each log level.]],
|
||||
Return_Value = [[]],
|
||||
Limitations = [[If you use log.msg in a callback, the name of the calling routine can't be determined. A solution
|
||||
is to include some means of reference such as the name of the callback as an argument, i.e.
|
||||
|
||||
log.msg(log.debug, "libPlugin.format_combobox:", "value is " .. self.value)
|
||||
|
||||
which would result in
|
||||
|
||||
DEBUG: callback: libPlugin.format_combobox: value is JPEG]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_log.msg(level, ...)
|
||||
if level.enabled then
|
||||
local args = {...}
|
||||
local call_level = 3
|
||||
if level == dtutils_log.always then
|
||||
call_level = args[1]
|
||||
table.remove(args, 1)
|
||||
end
|
||||
local log_msg = level.label
|
||||
if level.engine ~= dt_screen and call_level ~= 0 then
|
||||
log_msg = log_msg .. dtutils_log.caller(call_level) .. " "
|
||||
elseif log_msg:len() > 2 then
|
||||
log_msg = log_msg .. " "
|
||||
end
|
||||
for i = 1,#args do
|
||||
log_msg = log_msg .. tostring(args[i]) .. " "
|
||||
end
|
||||
level.engine(log_msg)
|
||||
end
|
||||
end
|
||||
|
||||
dtutils_log.libdoc.functions["log_level"] = {
|
||||
Name = [[log_level]],
|
||||
Synopsis = [[get or set the log level]],
|
||||
Usage = [[local log = require "lib/log"
|
||||
|
||||
local result = log.log_level(...)
|
||||
... - arguments - if none is supplied, then the current log level is returned as one of:
|
||||
log.debug, log.info, log.warn, log.error, log.success. If one of log.debug, log.info, log.warn,
|
||||
log.error, or log.success is supplied as the argument then the log level is set to that value. All
|
||||
log levels greater than or equal in value will be enabled. Any levels of lesser value will be disabled.]],
|
||||
Description = [[log_level gets and sets the logging level. When called with no arguments the current log level
|
||||
is returned as one of log.debug, log.info, log.warn, log.error, or log.success. When called with one of log.debug,
|
||||
log.info, log.warn, log.error or log.success then the log level is set. When setting the log level all levels
|
||||
equal or greater are enabled and any of lesser value are disabled. See the example.]],
|
||||
Return_Value = [[result - the log level, one of log.debug, log.info, log.warn, log.error or log.success]],
|
||||
Limitations = [[]],
|
||||
Example = [[Assume that the current log level is log.error. Calling log.log_level() will return log.error.
|
||||
Calling log.log_level(log.info) will leave log.debug disabled, and enable log.info, log.warn, log.error and
|
||||
log.success. log.info will be returned as the log_level.]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_log.log_level(...)
|
||||
local levels = {"debug", "info", "warn", "error", "success"}
|
||||
local args = {...}
|
||||
local log_level = nil
|
||||
if #args > 0 then
|
||||
log_level = args[1]
|
||||
if log_level == dtutils_log.critical or
|
||||
log_level == dtutils_log.screen or
|
||||
log_level == dtutils_log.always then
|
||||
-- these aren't valid for setting levels
|
||||
return nil
|
||||
else
|
||||
for _,v in ipairs(levels) do
|
||||
if dtutils_log[v].level >= log_level.level then
|
||||
dtutils_log[v].enabled = true
|
||||
else
|
||||
dtutils_log[v].enabled = false
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for _,v in ipairs(levels) do
|
||||
if dtutils_log[v].enabled == true then
|
||||
log_level = dtutils_log[v]
|
||||
end
|
||||
end
|
||||
end
|
||||
return log_level
|
||||
end
|
||||
|
||||
dtutils_log.libdoc.functions["engine"] = {
|
||||
Name = [[engine]],
|
||||
Synopsis = [[get and set the output engine]],
|
||||
Usage = [[local log = require "lib/dtutils.log"
|
||||
|
||||
result = log.engine(level, ...)
|
||||
level - table - the log level to get or set the engine for, one of log.debug, log.info, log.warn, log.error
|
||||
log.success, log.always, log.screen, log.critical
|
||||
... - function - the output function, one of dt.print, dt.print_error, dt.print_log, print
|
||||
if not function is included, the current engine is returned for the specified log level]],
|
||||
Description = [[engine returns the output engine for the specified log level if a second argument is not
|
||||
supplied. If a function is supplied as the second argment, then the output engine for the specified log level
|
||||
is set to that.]],
|
||||
Return_Value = [[result - function - the current output engine]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_log.print_engine(level, ...)
|
||||
local engines = {"dt_print", "dt_print_error", "dt_print_log", "print"}
|
||||
local args = {...}
|
||||
local cur_engine = ""
|
||||
if #args == 0 then
|
||||
cur_engine = level.engine
|
||||
else
|
||||
for _,v in ipairs(engines) do
|
||||
if args[1] == v then
|
||||
level.engine = args[1]
|
||||
cur_engine = level.engine
|
||||
end
|
||||
end
|
||||
end
|
||||
return cur_engine
|
||||
end
|
||||
|
||||
return dtutils_log
|
||||
173
lib/dtutils/string.lua
Normal file
173
lib/dtutils/string.lua
Normal file
@@ -0,0 +1,173 @@
|
||||
local dtutils_string = {}
|
||||
dtutils_string.libdoc = {
|
||||
Name = [[dtutils.string]],
|
||||
Synopsis = [[a library of string utilities for use in darktable lua scripts]],
|
||||
Usage = [[local ds = require "lib/dtutils.string"]],
|
||||
Description = [[This library contains string manipulation routines to aid in building
|
||||
darktable lua scripts.]],
|
||||
Return_Value = [[du - library - the darktable lua string library]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.]],
|
||||
Copyright = [[Copyright (c) 2016 Bill Ferguson <wpferguson@gmail.com>]],
|
||||
functions = {}
|
||||
}
|
||||
|
||||
dtutils_string.libdoc.functions["strip_accents"] = {
|
||||
Name = [[strip_accents]],
|
||||
Synopsis = [[strip accents from characters]],
|
||||
Usage = [[local ds = require "lib/dtutils.string"
|
||||
|
||||
local result = ds.strip_accents(str)
|
||||
str - string - the string with characters that need accents removed]],
|
||||
Description = [[strip_accents removes accents from accented characters returning the
|
||||
unaccented character.]],
|
||||
Return_Value = [[result - string - the string containing unaccented characters]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[Copied from https://forums.coronalabs.com/topic/43048-remove-special-characters-from-string/]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_string.strip_accents( str )
|
||||
local tableAccents = {}
|
||||
tableAccents["à"] = "a"
|
||||
tableAccents["á"] = "a"
|
||||
tableAccents["â"] = "a"
|
||||
tableAccents["ã"] = "a"
|
||||
tableAccents["ä"] = "a"
|
||||
tableAccents["ç"] = "c"
|
||||
tableAccents["è"] = "e"
|
||||
tableAccents["é"] = "e"
|
||||
tableAccents["ê"] = "e"
|
||||
tableAccents["ë"] = "e"
|
||||
tableAccents["ì"] = "i"
|
||||
tableAccents["í"] = "i"
|
||||
tableAccents["î"] = "i"
|
||||
tableAccents["ï"] = "i"
|
||||
tableAccents["ñ"] = "n"
|
||||
tableAccents["ò"] = "o"
|
||||
tableAccents["ó"] = "o"
|
||||
tableAccents["ô"] = "o"
|
||||
tableAccents["õ"] = "o"
|
||||
tableAccents["ö"] = "o"
|
||||
tableAccents["ù"] = "u"
|
||||
tableAccents["ú"] = "u"
|
||||
tableAccents["û"] = "u"
|
||||
tableAccents["ü"] = "u"
|
||||
tableAccents["ý"] = "y"
|
||||
tableAccents["ÿ"] = "y"
|
||||
tableAccents["À"] = "A"
|
||||
tableAccents["Á"] = "A"
|
||||
tableAccents["Â"] = "A"
|
||||
tableAccents["Ã"] = "A"
|
||||
tableAccents["Ä"] = "A"
|
||||
tableAccents["Ç"] = "C"
|
||||
tableAccents["È"] = "E"
|
||||
tableAccents["É"] = "E"
|
||||
tableAccents["Ê"] = "E"
|
||||
tableAccents["Ë"] = "E"
|
||||
tableAccents["Ì"] = "I"
|
||||
tableAccents["Í"] = "I"
|
||||
tableAccents["Î"] = "I"
|
||||
tableAccents["Ï"] = "I"
|
||||
tableAccents["Ñ"] = "N"
|
||||
tableAccents["Ò"] = "O"
|
||||
tableAccents["Ó"] = "O"
|
||||
tableAccents["Ô"] = "O"
|
||||
tableAccents["Õ"] = "O"
|
||||
tableAccents["Ö"] = "O"
|
||||
tableAccents["Ù"] = "U"
|
||||
tableAccents["Ú"] = "U"
|
||||
tableAccents["Û"] = "U"
|
||||
tableAccents["Ü"] = "U"
|
||||
tableAccents["Ý"] = "Y"
|
||||
|
||||
local normalizedString = ""
|
||||
|
||||
for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do
|
||||
if tableAccents[strChar] ~= nil then
|
||||
normalizedString = normalizedString..tableAccents[strChar]
|
||||
else
|
||||
normalizedString = normalizedString..strChar
|
||||
end
|
||||
end
|
||||
|
||||
return normalizedString
|
||||
|
||||
end
|
||||
|
||||
dtutils_string.libdoc.functions["escape_xml_characters"] = {
|
||||
Name = [[escape_xml_characters]],
|
||||
Synopsis = [[escape characters for xml documents]],
|
||||
Usage = [[local ds = require "lib/dtutils.string"
|
||||
|
||||
local result = ds.escape_xml_characters(str)
|
||||
str - string - the string that needs escaped]],
|
||||
Description = [[escape_xml_characters provides the escape sequences for
|
||||
"&", '"', "'", "<", and ">" with the corresponding "&",
|
||||
""", "'", "<", and ">".]],
|
||||
Return_Value = [[result - string - the string containing escapes for the xml characters]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
-- Keep & first, otherwise it will double escape other characters
|
||||
function dtutils_string.escape_xml_characters( str )
|
||||
|
||||
str = string.gsub(str,"&", "&")
|
||||
str = string.gsub(str,"\"", """)
|
||||
str = string.gsub(str,"'", "'")
|
||||
str = string.gsub(str,"<", "<")
|
||||
str = string.gsub(str,">", ">")
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
dtutils_string.libdoc.functions["urlencode"] = {
|
||||
Name = [[urlencode]],
|
||||
Synopsis = [[encode a string in a websage manner]],
|
||||
Usage = [[local ds = require "lib/dtutils.string"
|
||||
|
||||
local result = ds.urlencode(str)
|
||||
str - string - the string that needs to be made websafe]],
|
||||
Description = [[urlencode converts a string into a websafe version suitable for
|
||||
use in a web browser.]],
|
||||
Return_Value = [[result - string - a websafe string]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[https://forums.coronalabs.com/topic/43048-remove-special-characters-from-string/]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_string.urlencode(str)
|
||||
if (str) then
|
||||
str = string.gsub (str, "\n", "\r\n")
|
||||
str = string.gsub (str, "([^%w ])", function () return string.format ("%%%02X", string.byte()) end)
|
||||
str = string.gsub (str, " ", "+")
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
return dtutils_string
|
||||
22
locale/de_DE/LC_MESSAGES/calcDistance.po
Normal file
22
locale/de_DE/LC_MESSAGES/calcDistance.po
Normal file
@@ -0,0 +1,22 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:57+0100\n"
|
||||
"PO-Revision-Date: 2016-01-31 23:02+0100\n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Tobias Jakobs <tobias.jakobs@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.4\n"
|
||||
|
||||
#: calcDistance.lua:93
|
||||
msgid "Calculate the distance from latitude and longitude in km"
|
||||
msgstr "Errechne die Entfernung mit Hilfe des Längen- und Breitengrads in km"
|
||||
28
locale/de_DE/LC_MESSAGES/clear_GPS.po
Normal file
28
locale/de_DE/LC_MESSAGES/clear_GPS.po
Normal file
@@ -0,0 +1,28 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-09-07 22:25-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Bill Ferguson <wpferguson@gmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.5.4\n"
|
||||
"Language: de_DE\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-KeywordsList: gettext;dgettext:2;dcgettext:2ngettext:1,2;"
|
||||
"dngettext:2,3\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
|
||||
#: clear_GPS.lua:62
|
||||
msgid "clear GPS data"
|
||||
msgstr "GPS-Daten löschen"
|
||||
75
locale/de_DE/LC_MESSAGES/copy_attach_detach_tags.po
Normal file
75
locale/de_DE/LC_MESSAGES/copy_attach_detach_tags.po
Normal file
@@ -0,0 +1,75 @@
|
||||
# Holger Klemm, 2016.
|
||||
# Christian Kanzian <christian.kanzian@gmx.at>, 2016.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Lua image path\n"
|
||||
"POT-Creation-Date: 2016-01-07 20:33+0100\n"
|
||||
"PO-Revision-Date: 2016-10-18 15:34+0200\n"
|
||||
"Last-Translator: Christian Kanzian <christian.kanzian@gmx.at>\n"
|
||||
"Language-Team: German <kde-i18n-de@kde.org>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Lokalize 1.5\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Poedit-KeywordsList: \n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "Image tags copied ..."
|
||||
msgstr "Bild Tags kopiert ..."
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "No tags to attached, please copy tags first."
|
||||
msgstr "Es wurden keine Tags eingefügt. Bitte zuerst die Tags kopieren."
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "Tags attached ..."
|
||||
msgstr "Tags anhängen ..."
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "Tags removed from image(s)."
|
||||
msgstr "Tags von Bildern wurden gelöscht."
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "Tags replaced"
|
||||
msgstr "Tag ersetzen"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "multi copy tags"
|
||||
msgstr "Tags kopieren"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "paste tags"
|
||||
msgstr "Tags anhängen"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "replace tags"
|
||||
msgstr "Tags ersetzen"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "remove all tags"
|
||||
msgstr "Tags löschen"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "tag clipboard"
|
||||
msgstr "Zwischenablage"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "copy tags from selected image(s)"
|
||||
msgstr "Tags von ausgewählten Bildern kopieren"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "paste tags to selected image(s)"
|
||||
msgstr "Tags an ausgewählte Bilder anhängen"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "remove tags from selected image(s)"
|
||||
msgstr "Tags von ausgewählten Bildern löschen"
|
||||
|
||||
#: copy_attach_detach_tags.lua:10
|
||||
msgid "replace tags from selected image(s)"
|
||||
msgstr "Tags von ausgewählten Bildern ersetzen"
|
||||
|
||||
30
locale/de_DE/LC_MESSAGES/geo_uri.po
Normal file
30
locale/de_DE/LC_MESSAGES/geo_uri.po
Normal file
@@ -0,0 +1,30 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:58+0100\n"
|
||||
"PO-Revision-Date: 2016-01-31 23:13+0100\n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Tobias Jakobs <tobias.jakobs@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.4\n"
|
||||
|
||||
#: geo_uri.lua:55
|
||||
msgid "gnome-maps not found"
|
||||
msgstr "gnome-maps wurde nicht gefunden"
|
||||
|
||||
#: geo_uri.lua:81
|
||||
msgid "Command failed ..."
|
||||
msgstr "Aufruf fehlgeschlagen ..."
|
||||
|
||||
#: geo_uri.lua:88
|
||||
msgid "Open Location in Gnome Maps"
|
||||
msgstr "Öffnet den Ort in Gnome Maps"
|
||||
43
locale/de_DE/LC_MESSAGES/gimp.po
Normal file
43
locale/de_DE/LC_MESSAGES/gimp.po
Normal file
@@ -0,0 +1,43 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gimp\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-04-13 23:50-0400\n"
|
||||
"PO-Revision-Date: 2016-04-14 00:11-0500\n"
|
||||
"Last-Translator: Bill Ferguson <wpferguson@gmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.5.4\n"
|
||||
"Language: de_DE\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-KeywordsList: gettext;dgettext:2;dcgettext:2ngettext:1,2;"
|
||||
"dngettext:2,3\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
|
||||
#: gimp.lua:194
|
||||
#, lua-format
|
||||
msgid "Export Image %i/%i"
|
||||
msgstr "Exportiere Bild %i/%i"
|
||||
|
||||
#: gimp.lua:242
|
||||
msgid "Unable to move edited file into collection. Leaving it as %s"
|
||||
msgstr "Die bearbeitete Datei kann nicht in die Sammlung aufgenommen werden. Bearbeitete Datei ist %s"
|
||||
|
||||
#: gimp.lua:251
|
||||
msgid "GIMP not found"
|
||||
msgstr "GIMP nicht gefunden"
|
||||
|
||||
#: gimp.lua:266
|
||||
msgid "Launching GIMP..."
|
||||
msgstr "Starten von GIMP"
|
||||
|
||||
#: gimp.lua:313
|
||||
msgid "Edit with GIMP"
|
||||
msgstr "Bearbeiten mit GIMP"
|
||||
26
locale/de_DE/LC_MESSAGES/gps_select.po
Normal file
26
locale/de_DE/LC_MESSAGES/gps_select.po
Normal file
@@ -0,0 +1,26 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:58+0100\n"
|
||||
"PO-Revision-Date: 2016-01-31 23:05+0100\n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Tobias Jakobs <tobias.jakobs@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.4\n"
|
||||
|
||||
#: gps_select.lua:59
|
||||
msgid "Select all images with GPS information"
|
||||
msgstr "Markiert alle Bilder mit GPS-Informationen"
|
||||
|
||||
#: gps_select.lua:60
|
||||
msgid "Select all images without GPS information"
|
||||
msgstr "Markiert alle Bilder ohne GPS-Informationen"
|
||||
42
locale/de_DE/LC_MESSAGES/hugin.po
Normal file
42
locale/de_DE/LC_MESSAGES/hugin.po
Normal file
@@ -0,0 +1,42 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:59+0100\n"
|
||||
"PO-Revision-Date: 2016-01-31 23:09+0100\n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Tobias Jakobs <tobias.jakobs@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.4\n"
|
||||
|
||||
#: hugin.lua:73
|
||||
msgid "hugin not found"
|
||||
msgstr "Hugin wurde nicht gefunden"
|
||||
|
||||
#: hugin.lua:97
|
||||
msgid "Will try to stitch now"
|
||||
msgstr "Versuche Bilder zusammenzufügen"
|
||||
|
||||
#: hugin.lua:102
|
||||
msgid "Creating pto file"
|
||||
msgstr "Erzeuge pto Datei"
|
||||
|
||||
#: hugin.lua:105
|
||||
msgid "Running Assistent"
|
||||
msgstr "Starte den Assistenten"
|
||||
|
||||
#: hugin.lua:118
|
||||
msgid "Command hugin failed ..."
|
||||
msgstr "Aufruf von hugin fehlgeschlagen ..."
|
||||
|
||||
#: hugin.lua:124
|
||||
msgid "Hugin Panorama"
|
||||
msgstr "Hugin Panorama"
|
||||
46
locale/de_DE/LC_MESSAGES/kml_export.po
Normal file
46
locale/de_DE/LC_MESSAGES/kml_export.po
Normal file
@@ -0,0 +1,46 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Lua gettextTest\n"
|
||||
"POT-Creation-Date: 2016-01-22 15:33+0100\n"
|
||||
"PO-Revision-Date: 2016-01-22 23:28+0100\n"
|
||||
"Last-Translator: Tobias Jakobs <tobias.jakobs@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.8.4\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
"Plural-Forms: nplurals=2;t" plural=(n != 1);\n"
|
||||
"X-Poedit-KeywordsList: gettext;dgettext:2;dcgettext:2;ngettext:1,2;dngettext:2,3\n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
|
||||
msgid "KML/KMZ Export"
|
||||
msgstr "KML/KMZ Expor
|
||||
|
||||
msgid "Export Image %i/%i"
|
||||
msgstr "Exportiere Bild %i/%i"
|
||||
|
||||
msgid "KML export: Open KML/KMZ file after export"
|
||||
msgstr "KML export: KML/KMZ-Datei nach dem exportieren öffnen"
|
||||
|
||||
msgid "Opens the KML file after the export with the standard programm for KML files"
|
||||
msgstr "Öffnet die KML-Datei mit dem Standardprogram für KML-Dateien"
|
||||
|
||||
msgid "KML export: Export directory"
|
||||
msgstr "KML export: Export-Verzeichnis"
|
||||
|
||||
msgid "A directory that will be used to export the KML/KMZ files"
|
||||
msgstr "Verzeichnis in das die KML/KMZ-Dateien exportiert werden"
|
||||
|
||||
msgid "KML export: Connect images with path"
|
||||
msgstr "KML export: Bilder mit einer Linie verbinden"
|
||||
|
||||
msgid "connect all images with a path"
|
||||
msgstr "Verbinde alle Bilder mit einer Linie"
|
||||
|
||||
msgid "KML export: Create KMZ file"
|
||||
msgstr "KML export: KMZ-Datei erstellen"
|
||||
|
||||
msgid "Compress all imeges to one KMZ file"
|
||||
msgstr "Alle Bilder in einer KMZ-Datei komprimieren"
|
||||
84
locale/de_DE/LC_MESSAGES/quicktag.po
Normal file
84
locale/de_DE/LC_MESSAGES/quicktag.po
Normal file
@@ -0,0 +1,84 @@
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Christian Kanzian <christian.kanzian@gmx.at>, 2017.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-02-20 17:38+0100\n"
|
||||
"PO-Revision-Date: 2017-02-20 17:50+0100\n"
|
||||
"Last-Translator: Christian Kanzian <christian.kanzian@gmx.at>\n"
|
||||
"Language-Team: German <kde-i18n-de@kde.org>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Lokalize 1.5\n"
|
||||
|
||||
#: quicktag.lua:64
|
||||
msgid "max. length of button labels"
|
||||
msgstr "Max. Länge der Schaltflächen-Beschriftung"
|
||||
|
||||
#: quicktag.lua:65
|
||||
msgid "may range from 15 to 60 - needs a restart"
|
||||
msgstr "Bereich von 15 bis 60 möglich - Neustart erforderlich"
|
||||
|
||||
#: quicktag.lua:78
|
||||
msgid "number of quicktag fields"
|
||||
msgstr "Anzahl von quicktag Feldern"
|
||||
|
||||
#: quicktag.lua:79
|
||||
msgid "may range from 2 to 20 - needs a restart"
|
||||
msgstr "Bereich von 2 bis 20 möglich - Neustart erforderlich"
|
||||
|
||||
#: quicktag.lua:119
|
||||
#, lua-format
|
||||
msgid "quicktag %i is empty, please set a tag"
|
||||
msgstr "quicktag %i ist leer - bitte Tag eingeben"
|
||||
|
||||
#: quicktag.lua:134
|
||||
msgid "no images selected"
|
||||
msgstr "keine Bilder ausgewählt"
|
||||
|
||||
#: quicktag.lua:144
|
||||
#, lua-format
|
||||
msgid "tag \"%s\" attached to %i image(s)"
|
||||
msgstr "Tag \"%s\" an %i Bild(er) angehängt"
|
||||
|
||||
#: quicktag.lua:165
|
||||
msgid "old quicktag"
|
||||
msgstr "Alter quicktag"
|
||||
|
||||
#: quicktag.lua:166
|
||||
msgid "select the quicktag to replace"
|
||||
msgstr "Wähle den quicktag der ersetzt werden soll"
|
||||
|
||||
#: quicktag.lua:182 quicktag.lua:206
|
||||
msgid "new quicktag"
|
||||
msgstr "Neuer quicktag"
|
||||
|
||||
#: quicktag.lua:185
|
||||
msgid "enter your tag here"
|
||||
msgstr "Bitte Tag hier eingeben"
|
||||
|
||||
#: quicktag.lua:189
|
||||
msgid "set quicktag"
|
||||
msgstr "Setze Tag"
|
||||
|
||||
#: quicktag.lua:193
|
||||
msgid "new quicktag is empty!"
|
||||
msgstr "Neuer Tag ist leer!"
|
||||
|
||||
#: quicktag.lua:197
|
||||
#, lua-format
|
||||
msgid "quicktag \"%s\" replaced by \"%s\""
|
||||
msgstr "quicktag \"%s\" ersetzt durch \"%s\" "
|
||||
|
||||
#: quicktag.lua:246
|
||||
#, lua-format
|
||||
msgid "quicktag %i"
|
||||
msgstr "quicktag %i"
|
||||
|
||||
|
||||
BIN
locale/de_DE/LC_MESSAGES/select_untagged.mo
Normal file
BIN
locale/de_DE/LC_MESSAGES/select_untagged.mo
Normal file
Binary file not shown.
27
locale/de_DE/LC_MESSAGES/select_untagged.po
Normal file
27
locale/de_DE/LC_MESSAGES/select_untagged.po
Normal file
@@ -0,0 +1,27 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2017-09-14 12:18+0200\n"
|
||||
"PO-Revision-Date: 2017-09-14 12:23+0200\n"
|
||||
"Last-Translator: Jannis_V\n"
|
||||
"Language-Team: \n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.8.7.1\n"
|
||||
"X-Poedit-Basepath: ../../../contrib\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Poedit-SearchPath-0: select_untagged.lua\n"
|
||||
|
||||
#: select_untagged.lua:38
|
||||
msgid "select untagged images"
|
||||
msgstr "Auswahl von Bildern ohne Tags"
|
||||
|
||||
#: select_untagged.lua:64
|
||||
msgid "select untagged"
|
||||
msgstr "Ohne Tags"
|
||||
|
||||
#: select_untagged.lua:64
|
||||
msgid "select all images containing no tags or only tags added by darktable"
|
||||
msgstr "Wähle alle Bilder der aktuellen Sammlung aus, die keine Tags oder nur automatisch durch darktable erzeugte Tags enthalten"
|
||||
30
locale/de_DE/LC_MESSAGES/slideshowMusic.po
Normal file
30
locale/de_DE/LC_MESSAGES/slideshowMusic.po
Normal file
@@ -0,0 +1,30 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:59+0100\n"
|
||||
"PO-Revision-Date: 2016-01-31 23:13+0100\n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Tobias Jakobs <tobias.jakobs@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.4\n"
|
||||
|
||||
#: slideshowMusic.lua:80
|
||||
msgid "Slideshow background music file"
|
||||
msgstr "Diashow Hintergrundmusik-Datei"
|
||||
|
||||
#: slideshowMusic.lua:84
|
||||
msgid "Play slideshow background music"
|
||||
msgstr "Diashow Hintergrundmusik abspielen"
|
||||
|
||||
#: slideshowMusic.lua:85
|
||||
msgid "Plays music with rhythmbox if a slideshow starts"
|
||||
msgstr "Musik mit Rhythmbox abspielen, wenn die Diashow beginnt"
|
||||
22
locale/fr_FR/LC_MESSAGES/calcDistance.po
Normal file
22
locale/fr_FR/LC_MESSAGES/calcDistance.po
Normal file
@@ -0,0 +1,22 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:57+0100\n"
|
||||
"PO-Revision-Date: 2016-10-03 14:58+0200\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.9\n"
|
||||
|
||||
#: calcDistance.lua:93
|
||||
msgid "Calculate the distance from latitude and longitude in km"
|
||||
msgstr "Calcule la distance en km entre les positions GPS de deux images"
|
||||
27
locale/fr_FR/LC_MESSAGES/clear_GPS.po
Normal file
27
locale/fr_FR/LC_MESSAGES/clear_GPS.po
Normal file
@@ -0,0 +1,27 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-09-07 22:25-0400\n"
|
||||
"PO-Revision-Date: 2016-10-03 14:55+0200\n"
|
||||
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.8.9\n"
|
||||
"Language: fr_FR\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-KeywordsList: gettext;dgettext:2;dcgettext:2ngettext:1,2;"
|
||||
"dngettext:2,3\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
"Language-Team: \n"
|
||||
|
||||
#: clear_GPS.lua:62
|
||||
msgid "clear GPS data"
|
||||
msgstr "effacer données GPS"
|
||||
30
locale/fr_FR/LC_MESSAGES/geo_uri.po
Normal file
30
locale/fr_FR/LC_MESSAGES/geo_uri.po
Normal file
@@ -0,0 +1,30 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:58+0100\n"
|
||||
"PO-Revision-Date: 2016-10-03 15:05+0200\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.9\n"
|
||||
|
||||
#: geo_uri.lua:55
|
||||
msgid "gnome-maps not found"
|
||||
msgstr "gnome-maps non disponible"
|
||||
|
||||
#: geo_uri.lua:81
|
||||
msgid "Command failed ..."
|
||||
msgstr "La commande a échouée ..."
|
||||
|
||||
#: geo_uri.lua:88
|
||||
msgid "Open Location in Gnome Maps"
|
||||
msgstr "Ouvre la position dans Gnome Maps"
|
||||
42
locale/fr_FR/LC_MESSAGES/gimp.po
Normal file
42
locale/fr_FR/LC_MESSAGES/gimp.po
Normal file
@@ -0,0 +1,42 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gimp\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-04-13 23:50-0400\n"
|
||||
"PO-Revision-Date: 2016-10-03 15:06+0200\n"
|
||||
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.8.9\n"
|
||||
"Language: fr_FR\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-KeywordsList: gettext;dgettext:2;dcgettext:2ngettext:1,2;dngettext:2,3\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
"Language-Team: \n"
|
||||
|
||||
#: gimp.lua:194
|
||||
#, lua-format
|
||||
msgid "Export Image %i/%i"
|
||||
msgstr "Export image %i/%i"
|
||||
|
||||
#: gimp.lua:242
|
||||
msgid "Unable to move edited file into collection. Leaving it as %s"
|
||||
msgstr "Impossible de déplacer l'image modifiée dans la collection. L'image est %s"
|
||||
|
||||
#: gimp.lua:251
|
||||
msgid "GIMP not found"
|
||||
msgstr "GIMP n'est pas trouvé"
|
||||
|
||||
#: gimp.lua:266
|
||||
msgid "Launching GIMP..."
|
||||
msgstr "Lancement de GIMP..."
|
||||
|
||||
#: gimp.lua:313
|
||||
msgid "Edit with GIMP"
|
||||
msgstr "Éditer avec GIMP"
|
||||
26
locale/fr_FR/LC_MESSAGES/gps_select.po
Normal file
26
locale/fr_FR/LC_MESSAGES/gps_select.po
Normal file
@@ -0,0 +1,26 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:58+0100\n"
|
||||
"PO-Revision-Date: 2016-10-03 15:03+0200\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.9\n"
|
||||
|
||||
#: gps_select.lua:59
|
||||
msgid "Select all images with GPS information"
|
||||
msgstr "Sélectionner toutes les images avec des informations GPS"
|
||||
|
||||
#: gps_select.lua:60
|
||||
msgid "Select all images without GPS information"
|
||||
msgstr "Sélectionner toutes les images sans information GPS"
|
||||
42
locale/fr_FR/LC_MESSAGES/hugin.po
Normal file
42
locale/fr_FR/LC_MESSAGES/hugin.po
Normal file
@@ -0,0 +1,42 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:59+0100\n"
|
||||
"PO-Revision-Date: 2016-10-03 15:08+0200\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.9\n"
|
||||
|
||||
#: hugin.lua:73
|
||||
msgid "hugin not found"
|
||||
msgstr "hugin non disponible"
|
||||
|
||||
#: hugin.lua:97
|
||||
msgid "Will try to stitch now"
|
||||
msgstr "essayer de coller maintenant"
|
||||
|
||||
#: hugin.lua:102
|
||||
msgid "Creating pto file"
|
||||
msgstr "création du fichier pto"
|
||||
|
||||
#: hugin.lua:105
|
||||
msgid "Running Assistent"
|
||||
msgstr "lancement assistant"
|
||||
|
||||
#: hugin.lua:118
|
||||
msgid "Command hugin failed ..."
|
||||
msgstr "hugin a retourné une erreur"
|
||||
|
||||
#: hugin.lua:124
|
||||
msgid "Hugin Panorama"
|
||||
msgstr "panorama hugin"
|
||||
46
locale/fr_FR/LC_MESSAGES/kml_export.po
Normal file
46
locale/fr_FR/LC_MESSAGES/kml_export.po
Normal file
@@ -0,0 +1,46 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Lua gettextTest\n"
|
||||
"POT-Creation-Date: 2016-01-22 15:33+0100\n"
|
||||
"PO-Revision-Date: 2016-10-03 15:15+0200\n"
|
||||
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.8.9\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
"Plural-Forms: nplurals=2;t\" plural=(n != 1);\n"
|
||||
"X-Poedit-KeywordsList: gettext;dgettext:2;dcgettext:2;ngettext:1,2;dngettext:2,3\n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
|
||||
msgid "KML/KMZ Export"
|
||||
msgstr "export kml/kmz"
|
||||
|
||||
msgid "Export Image %i/%i"
|
||||
msgstr "export image %i/%i"
|
||||
|
||||
msgid "KML export: Open KML/KMZ file after export"
|
||||
msgstr "export kml: ouvrir le fichier kml/kmz après l'export"
|
||||
|
||||
msgid "Opens the KML file after the export with the standard programm for KML files"
|
||||
msgstr "ouvrir le fichier kml après l'export avec le lecteur kml par défaut"
|
||||
|
||||
msgid "KML export: Export directory"
|
||||
msgstr "export kml : répertoire pour l'export"
|
||||
|
||||
msgid "A directory that will be used to export the KML/KMZ files"
|
||||
msgstr "Le répertoire utiliser pour les fichiers d'export kml/kmz"
|
||||
|
||||
msgid "KML export: Connect images with path"
|
||||
msgstr "export kml: connecter les images avec un chemin"
|
||||
|
||||
msgid "connect all images with a path"
|
||||
msgstr "connecter toutes les images avec un chemin"
|
||||
|
||||
msgid "KML export: Create KMZ file"
|
||||
msgstr "export kml: créer un fichier kmz"
|
||||
|
||||
msgid "Compress all imeges to one KMZ file"
|
||||
msgstr "compresser les images en un fichier kmz"
|
||||
70
locale/fr_FR/LC_MESSAGES/pdf_slideshow.po
Normal file
70
locale/fr_FR/LC_MESSAGES/pdf_slideshow.po
Normal file
@@ -0,0 +1,70 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-04-10 09:30+0200\n"
|
||||
"PO-Revision-Date: 2017-04-10 09:48+0200\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.11\n"
|
||||
|
||||
#: pdf_slideshow.lua:55
|
||||
msgid "pdflatex not found"
|
||||
msgstr "pdflatex non trouvé"
|
||||
|
||||
#: pdf_slideshow.lua:63
|
||||
msgid "a pdf viewer"
|
||||
msgstr "lecteur pdf"
|
||||
|
||||
#: pdf_slideshow.lua:64
|
||||
msgid "can be an absolute pathname or the tool may be in the PATH"
|
||||
msgstr "peut être un chemin absolu où l'outil peut être dans le chemin par défaut"
|
||||
|
||||
#: pdf_slideshow.lua:68 pdf_slideshow.lua:95
|
||||
msgid "slideshow title"
|
||||
msgstr "titre du diaporama"
|
||||
|
||||
#: pdf_slideshow.lua:72
|
||||
msgid "transition delay (s)"
|
||||
msgstr "temporisation (s) entre diapositives"
|
||||
|
||||
#: pdf_slideshow.lua:83
|
||||
msgid "include image title"
|
||||
msgstr "inclus le titre de l'image"
|
||||
|
||||
#: pdf_slideshow.lua:85
|
||||
msgid "whether to include the image title (if defined) into the slide"
|
||||
msgstr "indique si le titre de l'image (si défini) doit être inclus dans la diapositive"
|
||||
|
||||
#: pdf_slideshow.lua:89
|
||||
msgid "include image author"
|
||||
msgstr "inclus l'auteur de l'image"
|
||||
|
||||
#: pdf_slideshow.lua:91
|
||||
msgid "whether to include the image author (if defined) into the slide"
|
||||
msgstr "indique si l'auteur de l'image (si défini) doit être inclus dans la diapositive"
|
||||
|
||||
#: pdf_slideshow.lua:167
|
||||
msgid "pdf slideshow"
|
||||
msgstr "diaporama pdf"
|
||||
|
||||
#: pdf_slideshow.lua:220
|
||||
msgid "problem running pdflatex"
|
||||
msgstr "impossible d'exécuter pdflatex"
|
||||
|
||||
#: pdf_slideshow.lua:221 pdf_slideshow.lua:231
|
||||
msgid "problem running "
|
||||
msgstr "impossible d'exécuter "
|
||||
|
||||
#: pdf_slideshow.lua:230
|
||||
msgid "problem running pdf viewer"
|
||||
msgstr "impossible d'exécuter le lecteur pdf"
|
||||
BIN
locale/fr_FR/LC_MESSAGES/select_untagged.mo
Normal file
BIN
locale/fr_FR/LC_MESSAGES/select_untagged.mo
Normal file
Binary file not shown.
27
locale/fr_FR/LC_MESSAGES/select_untagged.po
Normal file
27
locale/fr_FR/LC_MESSAGES/select_untagged.po
Normal file
@@ -0,0 +1,27 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2017-09-14 15:42+0200\n"
|
||||
"PO-Revision-Date: 2017-09-14 15:52+0200\n"
|
||||
"Last-Translator: Jannis_V\n"
|
||||
"Language-Team: \n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.8.7.1\n"
|
||||
"X-Poedit-Basepath: ../../../contrib\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Poedit-SearchPath-0: select_untagged.lua\n"
|
||||
|
||||
#: select_untagged.lua:38
|
||||
msgid "select untagged images"
|
||||
msgstr "sélection des images sans mots-clés"
|
||||
|
||||
#: select_untagged.lua:64
|
||||
msgid "select untagged"
|
||||
msgstr "sans mots-clés"
|
||||
|
||||
#: select_untagged.lua:64
|
||||
msgid "select all images containing no tags or only tags added by darktable"
|
||||
msgstr "sélection des images sans mots-clés ou seulement avec les mots-clés ajoutés par darktable"
|
||||
30
locale/fr_FR/LC_MESSAGES/slideshowMusic.po
Normal file
30
locale/fr_FR/LC_MESSAGES/slideshowMusic.po
Normal file
@@ -0,0 +1,30 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-31 22:59+0100\n"
|
||||
"PO-Revision-Date: 2016-10-03 15:10+0200\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.9\n"
|
||||
|
||||
#: slideshowMusic.lua:80
|
||||
msgid "Slideshow background music file"
|
||||
msgstr "musique pour le diaporama"
|
||||
|
||||
#: slideshowMusic.lua:84
|
||||
msgid "Play slideshow background music"
|
||||
msgstr "jouer la musique du diaporama"
|
||||
|
||||
#: slideshowMusic.lua:85
|
||||
msgid "Plays music with rhythmbox if a slideshow starts"
|
||||
msgstr "jouer la musique avec rhythmbox lorsque le diaporama démarre"
|
||||
@@ -31,7 +31,7 @@ local dt = require "darktable"
|
||||
local https = require "ssl.https"
|
||||
local cjson = require "cjson"
|
||||
|
||||
dt.configuration.check_version(...,{2,0,0})
|
||||
dt.configuration.check_version(...,{2,0,0},{3,0,0},{4,0,0})
|
||||
|
||||
-- compare two version strings of the form "major.minor.patch"
|
||||
-- returns -1, 0, 1 if the first version is smaller, equal, greater than the second version,
|
||||
|
||||
128
official/copy_paste_metadata.lua
Normal file
128
official/copy_paste_metadata.lua
Normal file
@@ -0,0 +1,128 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2016 Tobias Ellinghaus
|
||||
|
||||
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/>.
|
||||
]]
|
||||
--[[
|
||||
ADD SUPPORT FOR COPYING METADATA+RATING+COLOR LABELS+TAGS BETWEEN IMAGES
|
||||
This script adds keyboard shortcuts and buttons to copy/paste metadata between images.
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
* it adds buttons to the selected images module
|
||||
* it adds two keyboard shortcuts
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0})
|
||||
|
||||
-- set this to "false" if you don't want to overwrite metadata fields
|
||||
-- (title, description, creator, publisher and rights) that are already set
|
||||
local overwrite = true
|
||||
|
||||
local have_data = false
|
||||
local rating = 1
|
||||
local red = false
|
||||
local green = false
|
||||
local yellow = false
|
||||
local blue = false
|
||||
local purple = false
|
||||
local title = ""
|
||||
local description = ""
|
||||
local creator = ""
|
||||
local publisher = ""
|
||||
local rights = ""
|
||||
local tags = {}
|
||||
|
||||
local function copy(image)
|
||||
if not image then
|
||||
have_data = false
|
||||
else
|
||||
have_data = true
|
||||
rating = image.rating
|
||||
red = image.red
|
||||
green = image.green
|
||||
yellow = image.yellow
|
||||
blue = image.blue
|
||||
purple = image.purple
|
||||
title = image.title
|
||||
description = image.description
|
||||
creator = image.creator
|
||||
publisher = image.publisher
|
||||
rights = image.rights
|
||||
tags = {}
|
||||
for _, tag in ipairs(image:get_tags()) do
|
||||
if not (string.sub(tag.name, 1, string.len("darktable|")) == "darktable|") then
|
||||
table.insert(tags, tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function paste(images)
|
||||
if have_data then
|
||||
for _, image in ipairs(images) do
|
||||
image.rating = rating
|
||||
image.red = red
|
||||
image.green = green
|
||||
image.yellow = yellow
|
||||
image.blue = blue
|
||||
image.purple = purple
|
||||
if image.title == "" or overwrite then
|
||||
image.title = title
|
||||
end
|
||||
if image.description == "" or overwrite then
|
||||
image.description = description
|
||||
end
|
||||
if image.creator == "" or overwrite then
|
||||
image.creator = creator
|
||||
end
|
||||
if image.publisher == "" or overwrite then
|
||||
image.publisher = publisher
|
||||
end
|
||||
if image.rights == "" or overwrite then
|
||||
image.rights = rights
|
||||
end
|
||||
for _, tag in ipairs(tags) do
|
||||
image:attach_tag(tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dt.gui.libs.image.register_action(
|
||||
"copy metadata",
|
||||
function(event, images) copy(images[1]) end,
|
||||
"copy metadata of the first selected image"
|
||||
)
|
||||
|
||||
dt.gui.libs.image.register_action(
|
||||
"paste metadata",
|
||||
function(event, images) paste(images) end,
|
||||
"paste metadata to the selected images"
|
||||
)
|
||||
|
||||
dt.register_event(
|
||||
"shortcut",
|
||||
function(event, shortcut) copy(dt.gui.action_images[1]) end,
|
||||
"copy metadata"
|
||||
)
|
||||
|
||||
dt.register_event(
|
||||
"shortcut",
|
||||
function(event, shortcut) paste(dt.gui.action_images) end,
|
||||
"paste metadata"
|
||||
)
|
||||
@@ -1,93 +0,0 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2014 Jérémy Rosen
|
||||
|
||||
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/>.
|
||||
]]
|
||||
--[[
|
||||
DEBUG HELPERS
|
||||
A collection of helper functions to help debugging lua scripts.
|
||||
|
||||
require it as
|
||||
|
||||
dhelpers = require "official/debug-helpers"
|
||||
|
||||
Each function is documented in its own header
|
||||
|
||||
|
||||
]]
|
||||
|
||||
|
||||
local dt = require "darktable"
|
||||
local io = require "io"
|
||||
local table = require "table"
|
||||
require "darktable.debug"
|
||||
local M = {} -- The actual content of the module
|
||||
|
||||
--[[
|
||||
A function which prints its name and its parameters
|
||||
* name : the name of the tracepoint
|
||||
* ... : anything, will be dumped using dt.debug
|
||||
]]
|
||||
function M.tracepoint(name,...)
|
||||
print("***** "..name.." ****")
|
||||
params = {...}
|
||||
print(dt.debug.dump(params,"parameters"))
|
||||
return ...;
|
||||
end
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
A function that returns a tracepoint function with the given name
|
||||
This is mainly used to debug callbacks,
|
||||
|
||||
register_event(event, dhelpers.new_tracepoint("hit callback"))
|
||||
|
||||
will will print the following each time the callback is called
|
||||
|
||||
**** hit callback ****
|
||||
<all the callback's parameters dumped>
|
||||
|
||||
]]
|
||||
function M.new_tracepoint(name)
|
||||
return function(...) return M.tracepoint(name,...) end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Wrapper around debug.dump, will directly print to stdout,
|
||||
same calling covention
|
||||
]]
|
||||
|
||||
function M.dprint(...)
|
||||
print(dt.debug.dump(...))
|
||||
end
|
||||
|
||||
--[[
|
||||
sets dt.debug.known to shorten all images to a single line
|
||||
|
||||
if you don't need to debug the content of images, this will avoid them flooding your logs
|
||||
]]
|
||||
function M.terse_dump()
|
||||
for _,v in ipairs(dt.database) do
|
||||
dt.debug.known[v] = tostring(v)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
return M
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
|
||||
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;
|
||||
@@ -29,7 +29,7 @@ all tags longer than the given length will be automatically deleted at every res
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,0})
|
||||
dt.configuration.check_version(...,{2,0,0},{3,0,0},{4,0,0})
|
||||
|
||||
dt.preferences.register("delete_long_tags", "length", "integer",
|
||||
"maximum length of tags to keep",
|
||||
|
||||
168
official/enfuse.lua
Normal file
168
official/enfuse.lua
Normal file
@@ -0,0 +1,168 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2016 Tobias Ellinghaus
|
||||
|
||||
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/>.
|
||||
]]
|
||||
--[[
|
||||
RUN ENFUSE ON THE SELECTED IMAGES
|
||||
This script uses enfuse to merge the selected images into one tonemapped image and imports the result.
|
||||
It only works on ldr images (like, JPEG).
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
* it creates a new lighttable module
|
||||
|
||||
TODO
|
||||
* remember the exposure_mu value in config when the slider is moved
|
||||
* make the output filename unique so you can use it more than once per filmroll
|
||||
* find a less stupid way to make sure the float value of exposure_mu gets turned into a string
|
||||
with a decimal point instead of a comma in some languages
|
||||
* export images that are not ldr and remove them afterwards
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("enfuse",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("enfuse", msgid)
|
||||
end
|
||||
|
||||
-- add a new lib
|
||||
-- is enfuse installed?
|
||||
local enfuse_installed = df.check_if_bin_exists("enfuse")
|
||||
|
||||
-- initialize exposure_mu value and depth setting in config to sane defaults (would be 0 otherwise)
|
||||
if dt.preferences.read("enfuse", "depth", "integer") == 0 then
|
||||
dt.preferences.write("enfuse", "depth", "integer", 2)
|
||||
dt.preferences.write("enfuse", "exposure_mu", "float", 0.5)
|
||||
end
|
||||
|
||||
-- set up some widgets, initialized from config
|
||||
local exposure_mu = dt.new_widget("slider")
|
||||
{
|
||||
label = "exposure mu",
|
||||
tooltip = "center also known as MEAN of Gaussian weighting function (0 <= MEAN <= 1); default: 0.5",
|
||||
hard_min = 0,
|
||||
hard_max = 1,
|
||||
value = dt.preferences.read("enfuse", "exposure_mu", "float")
|
||||
}
|
||||
|
||||
local depth = dt.new_widget("combobox")
|
||||
{
|
||||
label = "depth",
|
||||
tooltip = "the number of bits per channel of the output image",
|
||||
value = dt.preferences.read("enfuse", "depth", "integer"),
|
||||
changed_callback = function(w) dt.preferences.write("enfuse", "depth", "integer", w.selected) end,
|
||||
"8", "16", "32"
|
||||
}
|
||||
|
||||
-- ... and tell dt about it all
|
||||
dt.register_lib(
|
||||
"enfuse", -- plugin name
|
||||
"enfuse", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
sensitive = enfuse_installed,
|
||||
exposure_mu,
|
||||
depth,
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = enfuse_installed and "run enfuse" or "enfuse not installed",
|
||||
clicked_callback = function (_)
|
||||
-- remember exposure_mu
|
||||
-- TODO: find a way to save it whenever the value changes
|
||||
local mu = exposure_mu.value
|
||||
dt.preferences.write("enfuse", "exposure_mu", "float", mu)
|
||||
|
||||
-- create a temp response file
|
||||
local response_file = os.tmpname()
|
||||
local f = io.open(response_file, "w")
|
||||
if not f then
|
||||
dt.print("error writing to `"..response_file.."'")
|
||||
os.remove(response_file)
|
||||
return
|
||||
end
|
||||
|
||||
-- add all filenames to the response file
|
||||
local cnt = 0
|
||||
local n_skipped = 0
|
||||
local target_dir
|
||||
for _, i in ipairs(dt.gui.action_images) do
|
||||
-- only use ldr files as enfuse can't open raws. alternatively we could export raws that we encounter
|
||||
if i.is_ldr then
|
||||
cnt = cnt + 1
|
||||
f:write(i.path.."/"..i.filename.."\n")
|
||||
target_dir = i.path
|
||||
else
|
||||
dt.print("skipping "..i.filename)
|
||||
n_skipped = n_skipped + 1
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
-- bail out if there is nothing to do
|
||||
if cnt == 0 then
|
||||
dt.print("no suitable images selected, nothing to do for enfuse")
|
||||
os.remove(response_file)
|
||||
return
|
||||
end
|
||||
if n_skipped == 1 then
|
||||
dt.print(n_skipped.." image skipped")
|
||||
elseif n_skipped > 1 then
|
||||
dt.print(n_skipped.." images skipped")
|
||||
end
|
||||
|
||||
-- call enfuse on the response file
|
||||
-- TODO: find something nicer
|
||||
local ugly_decimal_point_hack = string.gsub(string.format("%.04f", mu), ",", ".")
|
||||
-- TODO: make filename unique
|
||||
local output_image = target_dir.."/enfuse.tiff"
|
||||
local command = "enfuse --depth "..depth.value.." --exposure-mu "..ugly_decimal_point_hack
|
||||
.." -o \""..output_image.."\" \"@"..response_file.."\""
|
||||
if dt.control.execute( command) > 0 then
|
||||
dt.print("enfuse failed, see terminal output for details")
|
||||
os.remove(response_file)
|
||||
return
|
||||
end
|
||||
|
||||
-- remove the response file
|
||||
os.remove(response_file)
|
||||
|
||||
-- import resulting tiff
|
||||
local image = dt.database.import(output_image)
|
||||
|
||||
-- tell the user that everything worked
|
||||
dt.print("enfuse was successful, resulting image was imported")
|
||||
-- normally printing to stdout is bad, but we allow enfuse to show its output, so adding one extra line is ok
|
||||
print("enfuse: done, resulting image '"..output_image.."' was imported with id "..image.id)
|
||||
end
|
||||
}
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
-- kate: hl Lua;
|
||||
@@ -31,12 +31,14 @@ USAGE
|
||||
|
||||
-- TODO:
|
||||
-- * enable showing of the txt file (plugins/lighttable/draw_custom_metadata) if this script is enabled
|
||||
-- * maybe allow a lua command returning text instead of a command line call ? both ?
|
||||
-- * maybe allow a lua command returning text instead of a command line call? both?
|
||||
-- * make filenames with double quotes (") work
|
||||
|
||||
local dt = require "darktable"
|
||||
require "darktable.debug"
|
||||
dt.configuration.check_version(...,{2,1,0})
|
||||
require "official/yield"
|
||||
|
||||
dt.configuration.check_version(...,{2,1,0},{3,0,0},{4,0,0})
|
||||
|
||||
dt.preferences.register("generate_image_txt",
|
||||
"enabled",
|
||||
@@ -97,7 +99,7 @@ dt.register_event("mouse-over-image-changed", function(event, img)
|
||||
local command = command_setting:gsub("%$%(FILE_NAME%)", '"'..img_filename..'"')..' > "'..txt_filename..'"'
|
||||
|
||||
-- finally, run it
|
||||
coroutine.yield("RUN_COMMAND", command)
|
||||
dt.control.execute( command)
|
||||
end
|
||||
)
|
||||
|
||||
|
||||
@@ -28,9 +28,8 @@ This plugin will add a widget at the bottom of the left column in lighttable mod
|
||||
|
||||
|
||||
]]
|
||||
--[[
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,0})
|
||||
dt.configuration.check_version(...,{2,0,0},{3,0,0},{4,0,0})
|
||||
|
||||
local main_label = dt.new_widget("label"){selectable = true, ellipsize = "middle", halign = "start"}
|
||||
|
||||
@@ -59,6 +58,6 @@ dt.register_lib("image_path_no_ui","Selected Images path",true,false,{
|
||||
);
|
||||
|
||||
dt.register_event("mouse-over-image-changed",reset_widget);
|
||||
]]
|
||||
|
||||
--
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2015 Tobias Ellinghaus
|
||||
copyright (c) 2015-2016 Tobias Ellinghaus & Christian Mandel
|
||||
|
||||
darktable is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -50,6 +50,11 @@ dt.register_import_filter("prefer raw over jpeg", function(event, images)
|
||||
local current_base = ""
|
||||
local jpg_indices = {}
|
||||
local other_format_found = false
|
||||
|
||||
-- add dummy image to force processing for the last image
|
||||
local last_index
|
||||
table.insert(images, "")
|
||||
|
||||
for i, img in ipairs(images) do
|
||||
local extension = img:match("[^.]*$"):upper()
|
||||
local base = img:match("^.*[.]")
|
||||
@@ -73,7 +78,14 @@ dt.register_import_filter("prefer raw over jpeg", function(event, images)
|
||||
other_format_found = true
|
||||
end
|
||||
|
||||
last_index = i
|
||||
end
|
||||
|
||||
-- remove dummy image from list (just to make sure, it works even with keeping
|
||||
-- the dummy but that may break in the future), table.remove(images) does not
|
||||
-- work reliable because it can fail for sparse tables
|
||||
images[last_index] = nil
|
||||
|
||||
end)
|
||||
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
|
||||
|
||||
@@ -34,7 +34,7 @@ increase it if you need more temporary selection buffers
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,0})
|
||||
dt.configuration.check_version(...,{2,0,0},{3,0,0},{4,0,0})
|
||||
|
||||
local buffer_count = 5
|
||||
|
||||
@@ -45,7 +45,7 @@ for i=1,buffer_count do
|
||||
end,"save to buffer "..i)
|
||||
dt.register_event("shortcut",function()
|
||||
dt.gui.selection(saved_selection)
|
||||
end,"restor from buffer "..i)
|
||||
end,"restore from buffer "..i)
|
||||
end
|
||||
|
||||
local bounce_buffer = {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2015 Jérémy Rosen & Pascal Obry
|
||||
edited 2016 Tejovanth N
|
||||
|
||||
darktable is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -28,10 +29,14 @@ USAGE
|
||||
|
||||
This plugin will add a new exporter that will allow you to generate the pdf file
|
||||
|
||||
Plugin allows you to choose how many thumbnails you need per row
|
||||
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
dt.configuration.check_version(...,{2,0,0})
|
||||
require "official/yield"
|
||||
|
||||
dt.configuration.check_version(...,{2,0,0},{3,0,0},{4,0,0})
|
||||
|
||||
dt.preferences.register
|
||||
("selection_to_pdf","Open with","string",
|
||||
@@ -40,20 +45,29 @@ dt.preferences.register
|
||||
"xdg-open")
|
||||
|
||||
local title_widget = dt.new_widget("entry") {
|
||||
placeholder="Title"
|
||||
placeholder="Title"
|
||||
}
|
||||
local no_of_thumbs_widget = dt.new_widget("slider")
|
||||
{
|
||||
label = "Thumbs per Line",
|
||||
soft_min = 1, -- The soft minimum value for the slider, the slider can't go beyond this point
|
||||
soft_max = 10, -- The soft maximum value for the slider, the slider can't go beyond this point
|
||||
hard_min = 1, -- The hard minimum value for the slider, the user can't manually enter a value beyond this point
|
||||
hard_max = 10, -- The hard maximum value for the slider, the user can't manually enter a value beyond this point
|
||||
value = 4 -- The current value of the slider
|
||||
}
|
||||
local widget = dt.new_widget("box") {
|
||||
orientation=horizontal,
|
||||
dt.new_widget("label"){label = "Title:"},
|
||||
title_widget
|
||||
orientation=horizontal,
|
||||
dt.new_widget("label"){label = "Title:"},
|
||||
title_widget,
|
||||
dt.new_widget("label"){label = "Thumbnails per row:"},
|
||||
no_of_thumbs_widget
|
||||
}
|
||||
|
||||
local ending = [[
|
||||
\end{document}
|
||||
]]
|
||||
|
||||
local thumbs_per_line=4;
|
||||
local width = 1/thumbs_per_line-0.01
|
||||
|
||||
local filename =dt.configuration.tmp_dir.."/pdfout.tex"
|
||||
|
||||
@@ -93,23 +107,27 @@ dt.register_storage("export_pdf","Export thumbnails to pdf",
|
||||
local my_title = title_widget.text
|
||||
if my_title == "" then
|
||||
my_title = "Title"
|
||||
end
|
||||
local preamble = [[
|
||||
\documentclass[a4paper,10pt]{article}
|
||||
\usepackage{graphicx}
|
||||
\pagestyle{empty}
|
||||
\parindent0pt
|
||||
\usepackage{geometry}
|
||||
\geometry{a4paper,left=5mm,right=5mm, top=5mm, bottom=5mm}
|
||||
\begin{document}
|
||||
{\Large\bfseries ]]..my_title..[[} \\
|
||||
\bigskip\bigskip
|
||||
]]
|
||||
end
|
||||
local thumbs_per_line = no_of_thumbs_widget.value
|
||||
thumbs_per_line = tonumber(thumbs_per_line)
|
||||
width = (1/thumbs_per_line) - 0.01
|
||||
|
||||
local errmsg
|
||||
local latexfile
|
||||
latexfile,errmsg=io.open(filename,"w")
|
||||
if not latexfile then
|
||||
local preamble = [[
|
||||
\documentclass[a4paper,10pt]{article}
|
||||
\usepackage{graphicx}
|
||||
\pagestyle{empty}
|
||||
\parindent0pt
|
||||
\usepackage{geometry}
|
||||
\geometry{a4paper,left=5mm,right=5mm, top=5mm, bottom=5mm}
|
||||
\begin{document}
|
||||
{\Large\bfseries ]]..my_title..[[} \\
|
||||
\bigskip\bigskip
|
||||
]]
|
||||
|
||||
local errmsg
|
||||
local latexfile
|
||||
latexfile,errmsg=io.open(filename,"w")
|
||||
if not latexfile then
|
||||
error(errmsg)
|
||||
end
|
||||
my_write(latexfile,preamble)
|
||||
@@ -128,7 +146,7 @@ dt.register_storage("export_pdf","Export thumbnails to pdf",
|
||||
local dir=string.gsub(filename, "(.*/)(.*)", "%1")
|
||||
local locfile=string.gsub(filename, "(.*/)(.*)", "%2")
|
||||
local command = "pdflatex -halt-on-error -output-directory "..dir.." "..locfile
|
||||
local result = coroutine.yield("RUN_COMMAND",command)
|
||||
local result = dt.control.execute(command)
|
||||
if result ~= 0 then
|
||||
dt.print("Problem running pdflatex") -- this one is probably usefull to the user
|
||||
error("Problem running "..command)
|
||||
@@ -138,7 +156,7 @@ dt.register_storage("export_pdf","Export thumbnails to pdf",
|
||||
local pdffile=string.gsub(filename, ".tex", ".pdf")
|
||||
command = dt.preferences.read("selection_to_pdf","Open with","string")
|
||||
command = command.." "..pdffile
|
||||
local result = coroutine.yield("RUN_COMMAND",command)
|
||||
local result = dt.control.execute(command)
|
||||
if result ~= 0 then
|
||||
dt.print("Problem running pdf viewer") -- this one is probably usefull to the user
|
||||
error("Problem running "..command)
|
||||
|
||||
42
official/yield.lua
Normal file
42
official/yield.lua
Normal file
@@ -0,0 +1,42 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright 2016 by Tobias Jakobs.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
darktable yield compatibility script
|
||||
|
||||
USAGE
|
||||
* require this script from your main Lua file in the first line
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
local yield_orig = coroutine.yield
|
||||
|
||||
if (dt.configuration.api_version_major < 4) then
|
||||
dt.control = {}
|
||||
dt.control.execute = function(command)
|
||||
yield_orig("RUN_COMMAND", command)
|
||||
end
|
||||
dt.control.read = function(command)
|
||||
yield_orig("FILE_READABLE", command)
|
||||
end
|
||||
dt.control.sleep = function(command)
|
||||
yield_orig("WAIT_MS", command)
|
||||
end
|
||||
end
|
||||
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
-- kate: hl Lua;
|
||||
76
tools/get_lib_manpages.lua
Normal file
76
tools/get_lib_manpages.lua
Normal file
@@ -0,0 +1,76 @@
|
||||
--[[
|
||||
get_lib_manpages.lua - retrieve the included library documentation and output it as man pages
|
||||
|
||||
Copyright (c) 2016, Bill Ferguson
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local df = require "lib/dtutils.file"
|
||||
local log = require "lib/dtutils.log"
|
||||
local libname = nil
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0})
|
||||
|
||||
local keys = {"Name", "Synopsis", "Usage", "Description", "Return_Value", "Limitations",
|
||||
"Example", "See_Also", "Reference", "License", "Copyright"}
|
||||
|
||||
local function output_man(d)
|
||||
local name = d["Name"]
|
||||
if not libname then
|
||||
libname = name
|
||||
end
|
||||
local fname = "/tmp/" .. name .. ".3"
|
||||
local mf = io.open(fname, "w")
|
||||
if mf then
|
||||
mf:write(".TH " .. string.upper(name) .. " 3 \"\" \"\" \"Darktable " .. libname .. " functions\"\n")
|
||||
for _,section in ipairs(keys) do
|
||||
if d[section]:len() > 0 then
|
||||
mf:write(".SH " .. string.upper(string.gsub(section, "_", " ")) .. "\n")
|
||||
mf:write(d[section] .. "\n")
|
||||
end
|
||||
end
|
||||
mf:close()
|
||||
if df.check_if_bin_exists("groff") then
|
||||
if df.check_if_bin_exists("ps2pdf") then
|
||||
os.execute("groff -man " .. fname .. " | ps2pdf - " .. fname .. ".pdf")
|
||||
else
|
||||
log.msg(log.error, "Missing ps2pdf. Can't generate pdf man pages.")
|
||||
end
|
||||
else
|
||||
log.msg(log.error, "Missing groff. Can't generate pdf man pages.")
|
||||
end
|
||||
else
|
||||
log.msg(log.error, "Can't open file " .. fname .. "for writing")
|
||||
end
|
||||
end
|
||||
|
||||
-- find the libraries
|
||||
|
||||
local output = io.popen("cd "..dt.configuration.config_dir.."/lua/lib ;find . -name \\*.lua -print | sort")
|
||||
|
||||
-- loop through the libraries
|
||||
|
||||
for line in output:lines() do
|
||||
line = string.gsub(line, "/", ".")
|
||||
local lib_name = line:sub(3,-5)
|
||||
if lib_name:len() > 2 then
|
||||
lib_name = "lib/" .. lib_name
|
||||
local lib = require(lib_name)
|
||||
|
||||
-- print the documentation for the library
|
||||
if lib.libdoc then
|
||||
local doc = lib.libdoc
|
||||
if doc then
|
||||
output_man(doc)
|
||||
for _,fdoc in pairs(doc.functions) do
|
||||
|
||||
-- print the documentation for each of the functions
|
||||
output_man(fdoc)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
libname = nil
|
||||
end
|
||||
|
||||
49
tools/get_libdoc.lua
Normal file
49
tools/get_libdoc.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
--[[
|
||||
get_libdoc.lua - retrieve the included library documentation and output it
|
||||
|
||||
Copyright (c) 2016, Bill Ferguson
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0})
|
||||
|
||||
local keys = {"Name", "Synopsis", "Usage", "Description", "Return_Value", "Limitations",
|
||||
"Example", "See_Also", "Reference", "License", "Copyright"}
|
||||
|
||||
local function output_doc(d)
|
||||
for _,section in ipairs(keys) do
|
||||
if d[section]:len() > 0 then
|
||||
print(string.upper(string.gsub(section, "_", " ")))
|
||||
print("\t" .. d[section] .. "\n")
|
||||
end
|
||||
end
|
||||
print("\f")
|
||||
end
|
||||
|
||||
-- find the libraries
|
||||
|
||||
local output = io.popen("cd "..dt.configuration.config_dir.."/lua/lib ;find . -name \\*.lua -print | sort")
|
||||
|
||||
-- loop through the libraries
|
||||
|
||||
for line in output:lines() do
|
||||
line = string.gsub(line, "/", ".")
|
||||
local lib_name = line:sub(3,-5)
|
||||
if lib_name:len() > 2 then
|
||||
lib_name = "lib/" .. lib_name
|
||||
local lib = require(lib_name)
|
||||
|
||||
-- print the documentation for the library
|
||||
if lib.libdoc then
|
||||
local doc = lib.libdoc
|
||||
output_doc(doc)
|
||||
for _,fdoc in pairs(doc.functions) do
|
||||
|
||||
-- print the documentation for each of the functions
|
||||
output_doc(fdoc)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user