401 Commits

Author SHA1 Message Date
wpferguson
025f4299ea Merge pull request #435 from ddittmar/feature/select_non_existing
select non-existing images
2023-11-10 18:42:01 -05:00
ddittmar
dc50d40886 select non-existing images 2023-10-28 11:14:51 +02:00
wpferguson
867c49b1b9 Merge pull request #433 from wpferguson/script_manager_quote_fix
tools/script_manager - fixed quote
2023-10-17 16:04:42 -04:00
Bill Ferguson
13f02c95ea tools/script_manager - fixed quote with a " instead of s 2023-10-17 16:02:27 -04:00
wpferguson
d55dae73f0 Merge pull request #431 from wpferguson/script_manager_username_space_fix
Fix script_manager doesn't start on windows when username has a space
2023-10-16 22:44:10 -04:00
Bill Ferguson
34b9831472 tools/script_manager.lua - wrapped the scipt_dir in quotes to handle
the space in username problem on windows.
2023-10-16 22:41:12 -04:00
wpferguson
014a0fc1c9 Merge pull request #430 from wpferguson/explicitly_set_false
Explicitly set non running scriopts to FALSE
2023-09-20 13:54:02 -04:00
Bill Ferguson
bf17dccd69 tools/script_manager.lua - Explicitly set non running scriopts to FALSE
instead of relying on no value being intrepreted
                           as false.
2023-09-20 13:48:18 -04:00
wpferguson
890168d0d0 Merge pull request #426 from wpferguson/425_fix_executable_manager_not_appearing
Change window position of executable_manager and script_manager
2023-08-18 12:27:28 -04:00
Bill Ferguson
191aecacdf tools - Moved script_manager to bottom of lower left panel and
moved executable_manager to middle of lower left panel
        to work around windows GTK window placement error.
2023-08-18 12:23:24 -04:00
wpferguson
44ec01b136 Merge pull request #424 from wpferguson/fix_check_for_updates_comment
official/check_for_updates - updated instructions for additional modules
2023-07-17 17:41:10 -04:00
Bill Ferguson
a177da2040 official/check_for_updates = updated instructions for additional modules
required
2023-07-17 17:39:24 -04:00
wpferguson
c899040688 Merge pull request #423 from wpferguson/update_flatpak_readme
README.md - updated (fixed typos) echo commands to build luarc file
2023-07-16 13:24:56 -04:00
Bill Ferguson
e8af665b3c README.md - updated (fixed typos) echo commands to build luarc file 2023-07-16 13:22:06 -04:00
wpferguson
875734d455 Merge pull request #422 from wpferguson/fix_script_data_show
Fix script data show
2023-07-16 13:08:05 -04:00
Bill Ferguson
14d5a79db2 Added script_data.show to script_data blocks 2023-07-16 13:04:00 -04:00
Bill Ferguson
e1b2715ee9 contrib/gpx_export - added show function to script_data to fix #418 and #419. 2023-07-16 12:50:28 -04:00
wpferguson
7ecd5ca55d Merge pull request #421 from wpferguson/script_manager_updates
Script manager updates
2023-07-15 20:00:24 -04:00
wpferguson
931e7a4d37 Merge pull request #416 from wpferguson/check_max_api_version
Add check for max api version
2023-07-15 19:58:57 -04:00
Bill Ferguson
1792410efe lib/dtutils.lua - corrected test 2023-07-15 19:52:39 -04:00
wpferguson
8cbd9224f1 Merge pull request #377 from spaceChRiS/filter_manager_tooltip
Added tooltip to filter manager to better match new import scheme.
2023-07-14 17:40:10 -04:00
Bill Ferguson
24b552d619 lib/dtutils.lua - fix typo from testing 2023-07-12 13:18:01 -04:00
Bill Ferguson
68ae9326bf tools/script_manager.lua - added translation to preference setting 2023-07-12 12:37:14 -04:00
Bill Ferguson
b4626f2787 lib/dtutils.lua - changed log message to error on fail to load. Added
printing of the returned error.
2023-07-04 15:57:12 -04:00
Bill Ferguson
2fc8ed78e4 tools/script_manager.lua - added check to ensure script_data.show() runction
was passed as part of the script_data table
                           before trying to use it. Fixes #418.
2023-07-03 23:44:13 -04:00
Bill Ferguson
236b25a235 tools/script_manager.lua - clean up formatting and documentation 2023-07-03 23:42:47 -04:00
Bill Ferguson
a4f6af73ec tools/script_manager.lua - enabled log levels in callback routines 2023-06-24 19:56:51 -04:00
Bill Ferguson
fad74d6438 tools/script_manager.lua - cleaned up deactivate comments 2023-06-23 19:09:57 -04:00
Bill Ferguson
3e2d4c956d tools/script_manager.lua - Documented more fields for the sm.scripts table
Removed extra declaration of check_update preference
2023-06-22 16:04:26 -04:00
wpferguson
cd1d623390 Merge pull request #409 from dterrahe/x_touch_mimic
example slider mimic script for x-touch mini
2023-05-30 18:38:48 -04:00
Bill Ferguson
9e0374d597 lib/dtutils.lua - fix typos 2023-05-06 14:56:58 -04:00
Bill Ferguson
e47672b495 lib/dtutils.lua - added delay in check_min_api_version to sllow display of the error
message before script_manager displays an error message.
2023-05-06 14:36:22 -04:00
Bill Ferguson
1905be8577 lib/dtutils.lua - added check_max_api_version to handle the case where part of
the API is removed as in the case of the darktable.gui.libs.fixter functions.
2023-05-06 14:31:15 -04:00
dterrahe
4726821e7d Fix 8th knob 2023-05-05 23:19:00 +01:00
dterrahe
7ac37ddc1a Probe individual mask sliders 2023-05-03 23:43:01 +01:00
dterrahe
3889742e06 add x-touch mini script documentation 2023-03-25 18:49:55 +00:00
dterrahe
6bf3706455 add x-touch color calibration customisation 2023-03-25 18:14:19 +00:00
wpferguson
612ecaff6c Merge pull request #411 from wpferguson/doc_update
Updated documentation
2023-03-25 13:25:45 -04:00
Bill Ferguson
a85364ecff Updated documentation 2023-03-25 13:24:04 -04:00
wpferguson
c0af03c503 Merge pull request #408 from wpferguson/move_filename_string_functions_to_strings_library
Move filename/path/extension string functions to string library
2023-03-25 13:22:50 -04:00
wpferguson
9b61e4793b Merge pull request #410 from darktable-org/script_manager_running_script
script_manager darktable shortcut integration
2023-03-25 13:07:00 -04:00
wpferguson
75a19e74a0 Merge branch 'master' into script_manager_running_script 2023-03-25 13:03:55 -04:00
Bill Ferguson
21d7e72b0c tools/script_manager - resolved conflicts 2023-03-25 13:01:40 -04:00
Bill Ferguson
05d555eae5 tools/script_manager - let the shortcut system know that script_manager
widgets belong to script_manager
2023-03-25 12:46:59 -04:00
dterrahe
8e412c3eb3 example slider mimic script for x-touch mini 2023-03-25 10:59:19 -04:00
Bill Ferguson
80147a9ebc lib/dtugiles.file - Moved filename/path/extension string functions to string library. Left
the existing function calls in place for backward compatibility.  They
                      now call the new functions in the string library

lib/dtutils.string - Moved the filename/path/extension string functions to the string library
                     to prevent a circular library depnedency.  The file library includes the
                     string library.  If the string library needed to manipulate filename
                     related strings then the string library would need to include the
                     file library which would lead to a stack overflow.
2023-03-24 13:40:34 -04:00
Bill Ferguson
3f6a3e25f1 added global variable script+_manager_running_script to convey the script name to the darktable shortcut system 2023-03-16 20:59:10 -04:00
wpferguson
800cbeef9d Merge pull request #407 from wpferguson/script_manager_check_for_updates_pref
script_manager preference - check for updated scripts
2023-03-06 23:08:54 -05:00
Bill Ferguson
574f6a976f script_manager.lua - added a preference allowing the user to disable
automatic checking for updated scripts.
2023-03-06 22:56:55 -05:00
supertobi
030b360e57 Merge pull request #404 from darktable-org/macOS_HOME
Mac os home
2023-02-27 09:30:44 +01:00
supertobi
b883714192 fix HOME environment variable name for macOS 2023-02-27 09:29:16 +01:00
supertobi
d556f48168 fix HOME environment variable name for macOS 2023-02-27 09:27:25 +01:00
wpferguson
cd7cac0eec Merge pull request #403 from rds13/featureFixKML
fix HOME environment variable name
2023-02-25 12:21:41 -05:00
Romuald du Song
9b31f18470 fix HOME environment variable name 2023-02-25 17:41:45 +01:00
wpferguson
42a194b3d7 Merge pull request #400 from wpferguson/update_change_group_leader
Update contrib/change_group_leader
2022-10-16 14:03:16 -04:00
Bill Ferguson
6748a7125e README.md Added change_group_leader to contrib section
change_group_leader.lua  Updated copyright information

                         Added script_manager integration

                         Added i18n
2022-10-16 13:58:19 -04:00
wpferguson
c2b3f29f3b Merge pull request #399 from dexterlb/change_group_leader
Add group leader script
2022-10-15 13:02:49 -04:00
dexterlb
572a634d90 fix title 2022-10-10 21:39:38 +03:00
dexterlb
652606ffb0 fix copyright message 2022-10-10 21:38:30 +03:00
dexterlb
803537c63e add comments to change_group_leader 2022-10-09 12:00:29 +03:00
dexterlb
4648c2d9af add change_group_leader script 2022-10-09 11:54:02 +03:00
supertobi
edceb3e7e9 Merge pull request #396 from mpaglia0/master - Added Italian translation + improved label
Added Italian translation + improved label
2022-07-15 09:52:14 +02:00
Maurizio Paglia
da6bddcd77 Clear button now can be translated 2022-07-14 18:44:42 +02:00
Maurizio Paglia
b11f43e903 Added Italian translations 2022-07-14 18:43:02 +02:00
supertobi
ef218a4d2e Merge pull request #394 from manu-p/master
OpenInExplorer fr_FR translation
2022-06-28 10:02:45 +02:00
manu-p
b061f27e7a OpenInExplorer fr_FR translation 2022-06-27 17:36:47 +02:00
wpferguson
8e64d54ab1 Merge pull request #389 from wpferguson/fix_readme_links
[README.md] fix links to documentation
2022-03-16 13:12:59 -04:00
Bill Ferguson
56b40a80ac [README.md] fix links to documentation 2022-03-16 13:10:06 -04:00
supertobi
d359d8ecb4 Merge pull request #383 from dtorop/fuji-dr-load-only-with-exiftool
fujifilm_dynamic_range: only load if exiftool present
2022-02-14 09:58:13 +01:00
Dan Torop
2bc55d00bb fujifilm_dynamic_range: only load if exiftool present
Fail to load if exiftool binary is not present. This may be better
than silently (in UI) failing to read dyanmic range data.
2022-02-13 22:14:57 -05:00
wpferguson
d3cf564b71 Merge pull request #379 from wpferguson/cleanup_changelog
fix changelog formatting
2022-01-12 14:59:04 -05:00
Bill Ferguson
4fb8b97084 fix changelog formatting 2022-01-12 14:57:57 -05:00
wpferguson
32c5cceb1d Merge pull request #378 from wpferguson/update_docs
Updated documentation
2022-01-12 14:54:30 -05:00
Bill Ferguson
f38c14d6b5 Updated documentation 2022-01-12 14:53:02 -05:00
Christian Mandel
3070ca0bc4 Added tooltip to filter manager to better match new import scheme. 2022-01-09 15:46:29 +01:00
wpferguson
0122b3f717 Merge pull request #374 from wpferguson/which_deprecation_369
Fix dtutils/file.lua deprecation warning
2021-12-28 20:50:21 -05:00
Bill Ferguson
79678fd82f [lib/dtutils/file.lua] replaced deprecated "which" call with recommended
"command -v" call to check the existence of a
                       program
2021-12-28 20:45:50 -05:00
wpferguson
034ddfbafb Merge pull request #373 from wpferguson/deprecate_rename_tags
Deprecate rename-tags.lua
2021-12-28 11:22:25 -05:00
Bill Ferguson
aebd26d03a [lib/dtutils.lua] - Added deprecated function to use when deprecating
scripts

[contrib/rename-tags.lua] - Added deprecation warning
2021-12-27 19:36:15 -05:00
wpferguson
d609823491 Merge pull request #368 from wpferguson/fix_rename_sequence_leading_zeros
Fix rename_images sequence substitution
2021-10-22 11:15:19 -04:00
Bill Ferguson
8f414f783a [Changelog] Updated with latest changes 2021-10-22 11:12:24 -04:00
Bill Ferguson
2e521553bf [rename_images] Added changed suggested by Volker Bödker to make sure
sequence was 4 digits with leading zeros.
2021-10-22 11:09:01 -04:00
wpferguson
e93f5fc812 Merge pull request #365 from MarcusGama/devel
Update pt_BR translation
2021-10-04 13:48:37 -04:00
Marcus Gama
3dbbb7dfd5 Update pt_BR translation 2021-10-02 05:52:20 -03:00
supertobi
fbb92e1023 Initial pt_BR translation - Merge pull request #364 from MarcusGama/devel
Initial pt_BR translation
2021-09-29 09:54:15 +02:00
Marcus Gama
ea4c6373f9 Initial pt_BR translation 2021-09-28 20:08:30 -03:00
supertobi
40b2569a21 Merge pull request #363 from lbschenkel - Fix URL of Finnish police document
Fix URL of Finnish police document
2021-09-14 09:49:15 +02:00
Leonardo Brondani Schenkel
ef6e4d8f0b Fix URL of Finnish police document
The old URL returns a 404, this is the current replacement.
2021-09-13 17:23:10 +02:00
wpferguson
657407c7a0 Merge pull request #361 from chrisaga/master
Minor errors in dtutils_log.libdoc.Example : error levels are log.xxxx
2021-09-02 14:40:09 -04:00
chrisaga
b254577bc8 Merge branch 'master' of github.com:chrisaga/lua-scripts 2021-09-02 11:26:57 +02:00
chrisaga
63763bedc8 Minor errors in dtutils_log.libdoc.Example : error levels are log.xxxx 2021-09-02 11:23:36 +02:00
wpferguson
3d50d0fdb4 Merge pull request #360 from wpferguson/fix_AutoGroup_style_hide
Remove styles hiding from AutoGrouper.lua
2021-08-31 12:32:10 -04:00
Bill Ferguson
1ca6b18f5e Remove styles hiding from AutoGrouper.lua 2021-08-31 12:31:04 -04:00
wpferguson
b82ffc2af3 Merge pull request #359 from johnny-bit/dt3139_moar_guidez
add new script harmonic armature guide
2021-08-15 17:09:59 -04:00
Hubert Kowalski
d142151055 fix lines and contrib require 2021-08-15 22:43:13 +02:00
wpferguson
91b78529fe Merge pull request #341 from wpferguson/color_profile_manager
Add new script color_profile_manager
2021-08-15 16:35:19 -04:00
wpferguson
7aec6884f4 Merge pull request #351 from wpferguson/add_rename_script
[contrib/rename] Add rename_images script
2021-08-15 16:34:41 -04:00
Hubert Kowalski
7f140828bb add harmonic armature guide 2021-08-14 13:01:00 +02:00
supertobi
4f13a56814 Merge pull request #358 from chrisaga/master
Add french translation for photils.lua
2021-08-13 10:36:54 +02:00
chrisaga
27d3ea253d Add simple file header 2021-08-13 10:15:38 +02:00
chrisaga
5209b9899d Add french translation for photils 2021-08-13 10:06:58 +02:00
wpferguson
6a40c836c0 Merge pull request #355 from wpferguson/fix_geoToolbox
geoToolbox 5.4 compatibility
2021-08-05 18:55:08 -04:00
Bill Ferguson
c4df81bdef [contrib/geoToolbox] changed atan2 call to atan for lua 5.4 compatibility 2021-08-05 18:53:05 -04:00
wpferguson
e99827d237 Merge pull request #354 from wpferguson/fix_quicktag_entry
Fix quicktag tag entry
2021-08-04 11:30:44 -04:00
Bill Ferguson
a0cbe8efd5 [contrib/quicktag] changed is_password to false on entry so user can
see what they typed.
2021-08-04 11:28:12 -04:00
Bill Ferguson
b4ee97a768 got register_lib arguments in the correct order 2021-08-02 23:44:05 -04:00
Bill Ferguson
4bdaaaf1ce renamed rename to rename_images to better reflect the purpose 2021-08-02 23:26:22 -04:00
Bill Ferguson
3cf0fea092 [contrib/rename] Rename files including moving them into new or other
film rolls.
2021-07-28 01:18:51 -04:00
wpferguson
97e1451dc6 Merge pull request #348 from wpferguson/fix_script_manager_register_event
[script_manager] updated register_event syntax to API 7.0.0
2021-07-14 11:45:54 -04:00
Bill Ferguson
4cbb7724f2 [script_manager] updated register_event syntax to API 7.0.0 2021-07-14 11:44:27 -04:00
wpferguson
8f6c2c82d3 Merge pull request #345 from wpferguson/sm_git_branch_pattern_matching
Solution to pattern matching current git directory
2021-07-06 00:49:04 -04:00
Bill Ferguson
a6d5001e84 [script_manager] set logging to error 2021-07-06 00:46:21 -04:00
Bill Ferguson
6af180a313 [script_manager] after numerous attempts to solve this with just pattern
matching I realized the lack of conditionals made it
                 impossible to solve in that manner, so I resorted to
                 splitting the output into lines and testing each line.
2021-07-06 00:42:55 -04:00
wpferguson
86cc22d943 Merge pull request #344 from wpferguson/sm_fix_current_branch_not_detected
[script_manager] fix current branch not detected
2021-07-05 23:25:00 -04:00
Bill Ferguson
c1fbfd80d4 [script_manager] fix current branch not detected 2021-07-05 23:24:19 -04:00
wpferguson
040a83fb17 Merge pull request #343 from wpferguson/sm_i18n_git_fix
script_manager: fix i18n git integration
2021-07-05 23:05:46 -04:00
Bill Ferguson
2bc23a1446 [script_manager] fixed log message and reset log level for production 2021-07-05 23:01:30 -04:00
Bill Ferguson
97f4c911ed [script_manager] modified dirty repo test 2021-07-05 22:55:30 -04:00
Bill Ferguson
53d9a42a1a [script_manager] modified git interface to not rely on locale 2021-07-05 22:49:39 -04:00
wpferguson
c96c5234c4 Merge pull request #342 from wpferguson/sm_fix_no_current_branch
Script Manager crash on no current branch
2021-07-05 15:15:25 -04:00
Bill Ferguson
9a3eb7bc3e [script_manager] added check for nil current branch and dumped repo_data 2021-07-05 15:13:56 -04:00
Bill Ferguson
53f15ba8b8 [color_profile_manager] created color_profile_manager to manage the
darktable input and output color profiles
2021-07-02 21:51:20 -04:00
wpferguson
ecdc74b5a6 Merge pull request #340 from wpferguson/script_manager_final_bug_fix
Script manager final bug fix
2021-07-02 21:35:16 -04:00
Bill Ferguson
422fdbe2cf [script_manager] make sure we have branches before comparing 2021-07-02 21:33:50 -04:00
Bill Ferguson
887bc03f32 bug 2021-07-02 21:29:36 -04:00
Bill Ferguson
e32c0c70a8 bug 2021-07-02 21:28:26 -04:00
Bill Ferguson
440dfcadfd bug 2021-07-02 21:26:45 -04:00
Bill Ferguson
93cc0adb39 bug 2021-07-02 21:25:36 -04:00
Bill Ferguson
f8ec85ddae bug 2021-07-02 21:22:43 -04:00
wpferguson
688333f756 Merge pull request #339 from wpferguson/script_manager_no_branch_bugfix
[script_manager] handle the case where no branches are detected
2021-07-02 21:14:50 -04:00
Bill Ferguson
8f314059e4 [script_manager] handle the case where no branches are detected 2021-07-02 21:13:33 -04:00
wpferguson
9386a08af0 Merge pull request #338 from wpferguson/script_manager_noise
Stop script_manager "noise"
2021-07-02 20:30:32 -04:00
Bill Ferguson
19cd118f8f [script_manager] converted dt.print_log statements to log.msg with the appropriate level 2021-07-02 20:28:48 -04:00
Bill Ferguson
c196f98bc6 [ChangeLog.md] fixed formatting 2021-07-01 21:51:26 -04:00
Bill Ferguson
56ec258cc7 [script_manager] Added API awareness so that the correct version of
scripts that match the API version of darktable are
                 used.

[Changelog.md]   Updated the change log with the latest changes.
2021-07-01 21:45:38 -04:00
Bill Ferguson
5da64a7da0 [API 7.0.0 compatibility] Made changes to take advantage of the latest API changes
including the ability to start and stop most scripts.
2021-06-19 23:34:14 -04:00
wpferguson
f272aeb9fe Merge pull request #335 from wpferguson/revert-dt35-api-changes
Revert API 6.1+ compatibility changes.  Partially fixes #334
2021-06-19 16:38:36 -04:00
wpferguson
cf26b82d87 Merge pull request #336 from wpferguson/312_image_path_in_ui
Fix 312 - change case of image_path_in_ui module label
2021-06-19 14:01:46 -04:00
Bill Ferguson
2041fb34c9 [image_path_in_ui] changed case of module title to respect darktable
UI.
2021-06-19 13:57:39 -04:00
Bill Ferguson
4b63240ab9 Reverted register_event calls back to API 6.1.0 2021-06-11 21:44:38 -04:00
Bill Ferguson
9d59479049 Revert changes to accomadate API changes greater than 6.1 2021-06-11 19:47:43 -04:00
wpferguson
350cdfa436 Merge pull request #333 from wpferguson/fix_quicktag
Fix contrib/quicktag
2021-06-02 23:44:26 -04:00
Bill Ferguson
15de64dcf5 [contrib/quicktag] changed new entry field is_password setting to false
so that the user can see what they are typing.
2021-06-02 23:41:02 -04:00
wpferguson
d9c2519a83 Merge pull request #331 from wpferguson/fix_HDRmerge
Fix HDRmerge
2021-03-21 23:34:14 -04:00
wpferguson
9885e34f82 [Changelog] Added latest changes 2021-03-20 00:01:44 -04:00
wpferguson
ed0adfbae1 [Changelog] Added latest changes 2021-03-20 00:01:25 -04:00
wpferguson
beafb6c21b [HDRmerge] fixed crash resulting users renaming images from
camera default.

           Minor code cleanup
2021-03-19 23:52:28 -04:00
supertobi
7810791991 Merge pull request #330 from scheckmedia/master
photils plugin
2021-03-15 13:36:04 +01:00
Tobias Scheck
1d10d5234f refactore print method 2021-03-15 11:41:52 +01:00
Tobias Scheck
9900f46be4 add option to apply selected tags from a single image to multiple images (close #3); add setting parameter to enable/disable the export of an image before tag suggestion (close #5) 2021-03-15 11:36:37 +01:00
wpferguson
0d2c4dfb3a Merge pull request #329 from wpferguson/gimp_detached
GIMP run detached
2021-02-25 21:07:05 -05:00
wpferguson
752e27d97f Added detached mode to Edit with GIMP so that GIMP can keep running and accepting new images 2021-02-25 21:04:59 -05:00
wpferguson
33523fbe36 Merge pull request #328 from wpferguson/doc_updates
Update ChangeLog
2021-02-24 20:18:56 -05:00
wpferguson
db47dd5110 Update for Mark64/ext_editor_darkroom 2021-02-24 20:11:30 -05:00
wpferguson
a71097a8c9 Merge pull request #327 from Mark-64/ext_editor_darkroom
External editors: make gui visible in darkroom
2021-02-24 20:02:30 -05:00
U-DESKTOP-HQME86J\marco
931e448ebd more compact code 2021-02-24 19:21:32 +01:00
U-DESKTOP-HQME86J\marco
abd8bf4a60 option to display module in darkroom 2021-02-24 14:14:38 +01:00
U-DESKTOP-HQME86J\marco
aee1f82d04 restore current module registration 2021-02-23 07:40:30 +01:00
U-DESKTOP-HQME86J\marco
8112472df5 restore initialization line deleted by mistake 2021-02-22 08:17:46 +01:00
U-DESKTOP-HQME86J\marco
c9fa13b97b revert MAX_EDITORS to 9 2021-02-21 20:59:30 +01:00
U-DESKTOP-HQME86J\marco
73cd301ff4 ext_editor visible in darkroom 2021-02-21 20:55:23 +01:00
wpferguson
aa33ae0706 Merge pull request #325 from wpferguson/doc_updates
Update documentation
2021-02-19 17:30:13 -05:00
wpferguson
4e9c45c162 Updated README.md with links to the lua documentation. Added fujifilm_dynamic_range script to contrib. Added ChangeLog.md to keep track of recent changes. 2021-02-19 17:17:53 -05:00
wpferguson
f35cea75d3 Merge pull request #324 from wpferguson/API_6_2_3_register_action_changes
API 6.2.3 compatibility
2021-02-17 22:30:35 -05:00
wpferguson
4053ceb9e5 [API 6.2.3] Added changes for API 6.2.3 compatibility 2021-02-17 22:24:02 -05:00
wpferguson
448da35327 Merge pull request #322 from wpferguson/bugfix_select_untagged
fix select_untagged
2021-02-10 14:53:43 -05:00
wpferguson
6d9c4c90ba Used image table returned to callback, and returned selection table to caller so that it could do the selection. 2021-02-10 14:52:03 -05:00
wpferguson
5b8282e933 Merge pull request #321 from wpferguson/fix_6_2_1
Fix API 6.2.1 transition
2021-02-10 00:21:20 -05:00
wpferguson
b1498aa664 inline if didn't work. Added intermediate register_event function in transition library 2021-02-10 00:18:48 -05:00
wpferguson
2b28f021ee Merge pull request #320 from wpferguson/fix_6_2_2
fix API 6.2.2 compatibility
2021-02-09 23:38:56 -05:00
wpferguson
dc4938c377 inline if didn't work. Replaced with full if/else 2021-02-09 23:36:54 -05:00
wpferguson
69b846a856 Merge pull request #319 from wpferguson/api_6_2_2_register_selection
API 6.2.2 compatibility
2021-02-04 23:07:56 -05:00
wpferguson
1460396d4f [contrib/select_untagged] Added check for API 6.2.2 and added name
field to register_selection if it is
2021-02-04 23:04:21 -05:00
wpferguson
b9804d9d48 Merge pull request #318 from wpferguson/api_6_2_1_lua_event_changes
Added compatibility for lua events API change 6.2.1.
2021-02-01 18:31:19 -05:00
wpferguson
ade7008127 Added compatibility for lua events API change 6.2.1. 2021-02-01 18:27:20 -05:00
wpferguson
da0235ab27 Merge pull request #317 from wpferguson/correct_api_version
[dtutils] fixed api version check
2021-01-21 21:36:36 -05:00
wpferguson
c17e832572 [dtutils] fixed api version check 2021-01-21 21:34:43 -05:00
wpferguson
c12d6f01e7 Merge pull request #316 from wpferguson/add_database_get_image_to_find_by_image_id
add database.get_image() to dutils.find_image_by_id()
2021-01-21 21:26:08 -05:00
wpferguson
778ad2ce20 [dtutils] added database.get_image() to find_image_by_id() for users with lua API 6.2.0 or greater 2021-01-21 21:20:17 -05:00
wpferguson
33ef58ac97 Merge pull request #315 from schwerdf/master
New utility function for random image access by ID.
2021-01-19 17:37:23 -05:00
wpferguson
8091ba9398 Merge pull request #313 from chrisaga/master
copy_attach_detachtags.lua localization proposal
2021-01-19 14:20:09 -05:00
August Schwerdfeger
8e0bf9e991 Corrected names in documentation of 'find_image_by_id'. 2021-01-19 01:34:36 -06:00
August Schwerdfeger
daee82aa6a Added a new utility function to look up an image in the database by ID. 2021-01-19 01:05:50 -06:00
chrisaga
359a1fa3a9 As discussed, localize module name in GUI 2021-01-10 19:54:10 +01:00
wpferguson
021b32c1d4 Merge pull request #314 from dtorop/fujifilm-dynamic-range
Fujifilm dynamic range
2021-01-07 23:38:11 -05:00
Dan Torop
1d1eb6a768 fujifilm-dynamic-range: remove translations, use full exiftool path
Suggestions as per @wpferguson:

"We came to a decision a while ago to not translate debug
messages(i.e. print_log() and print_error()) because the author might
not be able to read the output if it was a language they weren't
familiar with. So the only thing we translate are labels, tooltips,
and dt.print() messages."

"df.check_if_bin_exists() returns the full path to the
executable. Using the returned value would let the script run on
windows and macos too."
2021-01-07 22:55:55 -05:00
Dan Torop
a9fd0718b0 fujifilm-dynamic-range: skip non-Fuji non-raws and clean-ups
Don't bother to pull EXIF data unless the imported image is a Fujifilm
raw.

Cleanup conditionals for error handling by immediately returning on
failure, avoiding deep nested conditionals.

Cleanup log/error output with clearer messages.
2021-01-06 11:21:05 -05:00
Dan Torop
6341a0a459 fujifilm-dynamic-range: better logging and error handling
Use dt.print_log() for log messages. Print error output if no results
or results not parseable from exiftool.

Also update the date, it's 2021.
2021-01-06 10:28:52 -05:00
Dan Torop
952b8b2eb2 fujifilm-dynamic-range: better exiftool parsing & comments
Get tab-delimited output from exiftool without rounding.  Add comments
to explain the choice of tags being read and tool to read them.
2021-01-06 10:11:59 -05:00
Dan Torop
b08d7f27d2 fujifilm_dynamic_range : first draft
Read RawExposureBias and adjust "EXIF Exposure Bias" based upon this.
2021-01-05 22:38:27 -05:00
chrisaga
a71889021f add copy_attach_detachtags.po header 2021-01-03 21:02:25 +01:00
chrisaga
367631af99 copy_attach_detachtags.lua localization 2021-01-03 20:55:45 +01:00
wpferguson
62753d1a50 Merge pull request #309 from chrisaga/master
script_manager.lua function clear_button reset tooltip
2020-12-29 11:12:26 -05:00
chrisaga
dfe589e843 script_manager.lua function clear_button must reset tooltip too 2020-12-28 19:18:35 +01:00
wpferguson
d09574bb17 Merge pull request #308 from chrisaga/master
script_manager.lua localization proposal (including minor changes in …
2020-12-28 12:50:14 -05:00
chrisaga
2be12e5b37 script_manager.lua localization proposal (including minor changes in the script) 2020-12-28 10:42:15 +01:00
wpferguson
d2086b6480 Merge pull request #307 from chrisaga/master
More clear_GPS.lua localization
2020-12-27 20:40:09 -05:00
chrisaga
132668d04c More clear_GPS.lua localization 2020-12-27 18:22:19 +01:00
wpferguson
0a4aa95965 Merge pull request #306 from chrisaga/localize-copy_paste_metadata.lua
Localize copy paste metadata.lua
2020-12-26 17:18:21 -05:00
wpferguson
af1e774d1f Merge pull request #305 from chrisaga/patch-1
Update clear_GPS.lua
2020-12-26 17:16:45 -05:00
chrisaga
e5271d79a0 Localize copy_paste_metadata.lua 2020-12-26 13:27:53 +01:00
chrisaga
dfd714540e fr po-file for copy_paste_metadata 2020-12-26 13:21:00 +01:00
chrisaga
a0fd22be19 Update clear_GPS.lua
Changed first line of header which was referring to another plugin (gimp.lua)
2020-12-26 12:08:38 +01:00
wpferguson
c0dc55b18e Merge pull request #303 from wpferguson/script_manager_3.4
Merry Christmas aka new script manager
2020-12-24 09:56:22 -05:00
wpferguson
258def1cf8 Merge pull request #304 from wpferguson/logging_cleanup
Reduce the amount of trace information in log messages.
2020-12-24 09:55:24 -05:00
wpferguson
b2095fd947 Merge pull request #299 from wpferguson/fix_check_if_bin_exists_and_executable_paths
Fix check if bin exists and executable paths
2020-12-24 09:54:52 -05:00
wpferguson
e51278f76e Merge branch 'master' into fix_check_if_bin_exists_and_executable_paths 2020-12-24 09:54:29 -05:00
wpferguson
9af60063d8 [dtutils.log] changed the amount of trace information based on log level.
script, caller, line number, msg were printed for all log
              levels, but changed to return less info except in log.debug
              log.error, and log.critical messages.  All messages print
              the script name except for log.screen.
2020-12-23 22:52:46 -05:00
wpferguson
37b523f989 [script_manager] rewrite of script_manager for a better fit with
darktable 3.4 and the new scripts_installer. Changed
                 interface to paged instead of scrolled so that less
                 screen space is required.  Added capability to
                 install/update external repositories and seamlessly
                 integrate with script_manager.
2020-12-23 19:54:16 -05:00
wpferguson
96ba075e86 Removed debugging statement 2020-12-22 22:00:41 -05:00
Bill Ferguson
e86a3a3236 [file.lua] removed debugging statement 2020-12-18 11:08:12 -05:00
wpferguson
622a9ebab4 Merge pull request #301 from pitbuster/master
Sanitize filename in fujifilm_ratings.lua to handle spaces.
2020-12-14 00:07:28 -05:00
Felipe Contreras
b26ec58f9f Sanitize filename in fujifilm_ratings.lua to handle spaces. 2020-12-13 22:00:39 -03:00
Bill Ferguson
b41b2a1566 [file.lua] Added create_tmp_file() back in. 2020-11-30 21:51:55 -05:00
Bill Ferguson
b893383353 [file.lua] Added comments for local functions. Added more API
documentation for check_if_bin_exists.  Added save
           executable path preference to _search_for_nix_bin()
           so that we can save a little time on subsequent runs
           once an executable is found.
2020-11-30 21:06:57 -05:00
Bill Ferguson
ddcf3ed3f4 [file.lua] fixed line trimming in macos search for bin 2020-11-29 18:41:32 -05:00
Bill Ferguson
da05748393 [file.lua] combined the is_ functions into file_test() which allows
testing to see if a path exists, is a dir, is a file, or
	   is executable.  Added case insensitive search to the
           windows search for bin.
2020-11-29 18:00:47 -05:00
wpferguson
976ad4ab17 Merge pull request #297 from wpferguson/remove_yield
Remove official/yield.lua
2020-11-26 19:08:57 -05:00
wpferguson
8dcf19834c Merge pull request #298 from wpferguson/add_sanitize_decimals_to_enfuseAdvanced
Add sanitize_decimals() to enfuseAdvanced
2020-11-25 19:42:25 -05:00
Bill Ferguson
43315bb9cc [enfuseAdvanced] added a sanitize_decimals function to sanitize the command lines. 2020-11-25 19:39:49 -05:00
Bill Ferguson
4ab20d8088 Removed official/yield.lua since it was only required for lua API 3.x and less (darktable 2.0 and before). Removed require from scripts that had included it. 2020-11-21 18:33:26 -05:00
wpferguson
485de32bb4 Merge pull request #294 from wpferguson/fix_enfuseAdvanced_decimal_separator
fix enfuseAdvanced crash on comma decimal separator
2020-11-19 00:38:06 -05:00
Bill Ferguson
536b9726b2 [enfuseAdvanced] Added check for comma decimal separator. Replace with period if found. 2020-11-19 00:35:17 -05:00
wpferguson
41ae43e741 Merge pull request #293 from wpferguson/fix_multi_os_crash
Fix multi_os crash
2020-11-17 19:05:23 -05:00
Bill Ferguson
b5b49245fa Needlessly creating the executable path preference for the ufraw-batch executable on linux caused the value to be saved as the startup directory, which in turn caused a crash when trying to extract the embedded jpg. 2020-11-17 18:53:54 -05:00
wpferguson
d172ac7049 Merge pull request #290 from wpferguson/get_enfusedAdvanced_running
Fix translation in enfuseAdvanced
2020-09-16 00:10:23 -04:00
Bill Ferguson
8808306a5f [contrib/enfusedAdvanced.lua] _ was declared globally in a subroutine, thus negating translation once the subroutine was called. 2020-09-16 00:05:53 -04:00
wpferguson
00dac577f6 Merge pull request #289 from wpferguson/fix_enfuseAdvanced_print_error
fix print errors in enfuseAdvanced - this takes care of the current last comment in #186
2020-09-14 20:56:10 -04:00
Bill Ferguson
85c218b350 [contrib/enfuseAdvanced] fixed translations in for loops by changing the unused variable _ to x so that the translation function _() doesn't get replaced with the value of _. 2020-09-14 20:47:29 -04:00
supertobi
6f71ef4d6b Merge pull request #288 from homer3018/patch-1
Typo
2020-09-11 16:04:19 +02:00
homer3018
394cbdb454 Typo 2020-09-11 00:05:22 +02:00
supertobi
62e2a7969d Merge pull request #287 from darktable-org/supertobi-patch-1
README - suggested update #286 by heviiguy
2020-09-06 20:13:28 +02:00
supertobi
d9c284b54d Update README.md
README - suggested update #286
2020-09-06 20:01:34 +02:00
wpferguson
bd2bc3659c Merge pull request #284 from wpferguson/update_install_instructions
Update README.md
2020-08-23 00:40:49 -04:00
Bill Ferguson
f76fa01240 Updated install instructions adding flatpak information and updating preferred way to enable and disable scripts. 2020-08-18 12:08:23 -04:00
wpferguson
45d62f418c Merge pull request #283 from wpferguson/script_manager_collapsible
script_manager resizes smaller
2020-08-16 15:59:06 -04:00
Bill Ferguson
650325257b made button a local variable 2020-08-16 15:57:41 -04:00
Bill Ferguson
a0a8a61d56 Enabled latest dev changes in lua API so that script_manager responds better to panel resizing 2020-08-16 15:52:57 -04:00
wpferguson
998732287b Merge pull request #282 from wpferguson/change_name_to_id
Fix script_manager not starting (and any other register_lib module)
2020-08-16 13:21:48 -04:00
Bill Ferguson
509c7da384 changed darktable.current_view().name to darktable.current_view().id for view check because name is translated and id is not. 2020-08-16 13:18:27 -04:00
wpferguson
771e03ce76 Merge pull request #279 from wpferguson/278_fix_register_lib_hang
278 fix register lib hang
2020-08-14 20:23:54 -04:00
supertobi
7d9b8dbdda Update video_ffmpeg.lua
added more framerate options
2020-08-10 15:21:25 +02:00
Bill Ferguson
912ae93d10 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 01:56:48 -04:00
Bill Ferguson
b613cc266a Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 01:56:38 -04:00
Bill Ferguson
dfb8d4e7c6 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 01:48:36 -04:00
Bill Ferguson
b8e3ec4302 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 01:43:31 -04:00
Bill Ferguson
d4e21b7877 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 01:43:20 -04:00
Bill Ferguson
d10cf968f1 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 01:33:09 -04:00
Bill Ferguson
880ec7fc58 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 01:26:45 -04:00
Bill Ferguson
432b6b5dd7 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 01:21:17 -04:00
Bill Ferguson
5f5ad426bf Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 01:01:33 -04:00
Bill Ferguson
52acdb9036 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 00:45:27 -04:00
Bill Ferguson
138b23674a Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 00:36:43 -04:00
Bill Ferguson
8a710798f7 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-09 00:17:34 -04:00
Bill Ferguson
be5557080b Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-08 23:38:48 -04:00
Bill Ferguson
994b9c6472 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-08 23:28:50 -04:00
Bill Ferguson
d05536bed4 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-08 23:21:54 -04:00
Bill Ferguson
cc4f1d7251 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-08 23:11:33 -04:00
Bill Ferguson
2ddd0965cd Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-08 22:40:54 -04:00
Bill Ferguson
4e68e420cc removed debugging print statemant 2020-08-08 22:40:40 -04:00
Bill Ferguson
50be8a9240 Added check to ensure we are in lighttable view prior to callling darktable.register_lib. If darktable was invoked in single image (darkroom) mode, then an event handler is registered to detect the change from darkroom mode to lighttable mode so that we can then call darktable.register_lib. 2020-08-08 22:11:29 -04:00
Bill Ferguson
d1e330eee0 Added check to ensure we are in lighttable view prior to callling
darktable.register_lib.  If darktable was invoked in single
image (darkroom) mode, then an event handler is registered to
detect the change from darkroom mode to lighttable mode so that
we can then call darktable.register_lib.
2020-08-08 22:01:58 -04:00
supertobi
6aa5893b5f Merge pull request #277 from wpferguson/update_readme
Update README.md
2020-08-04 21:32:43 +02:00
Bill Ferguson
797d668ebb Fixed typo 2020-08-04 11:11:41 -04:00
Bill Ferguson
b23595ff54 Added latest scripts 2020-08-03 18:52:28 -04:00
Bill Ferguson
7790c9700b Separated check_if_bin_exists() into 2 functions, one to check the returned
executable_path variable and another to search for the binary if it
wasn't detected.  All results are checked to ensure they exist, are
a file, and are executable.  This fixes the situation where a directory
gets stored as an executable path. Search functions are operating system
specific.  Executable paths are saved for MacOS and Windows to make for
shorter searches in the future.
2020-08-02 18:33:49 -04:00
wpferguson
6903f8385b Merge pull request #267 from wpferguson/image_time
Image time
2020-08-02 17:36:24 -04:00
wpferguson
e8205c6435 Merge pull request #276 from wpferguson/executable_manager_feedback
executable_manager provide feedback
2020-08-02 17:35:03 -04:00
Bill Ferguson
1d5192a39c removed duplpicate copies of the is_ functions 2020-07-21 20:40:22 -04:00
Bill Ferguson
04525e1258 Added functions is_file(path), is_dir(path, and is_executable(path) to
ensure a path is a file, directory, or executable respectively.  Replaced
code in check_if_bin_exists with these functions to reduce the lines of code
and make it more understandable.
2020-07-21 16:39:34 -04:00
wpferguson
c114856699 Merge pull request #275 from camjc/patch-1
Typo in README
2020-07-19 15:39:58 -04:00
Bill Ferguson
57a9a8495c made newly added strings translatable 2020-07-19 15:38:08 -04:00
Bill Ferguson
53bb95e6fd Added entry widget to display the current setting, or a placeholder specifying what would or should happen depending on operating system to provide visual feedback. 2020-07-19 13:13:24 -04:00
Cameron Chamberlain
a40fe3099d Typo in README 2020-07-19 10:54:48 +10:00
wpferguson
8d38ea0cf2 Merge pull request #270 from Mark-64/RL_output_sharpen_doc
add doc comments to RL_output_sharpen
2020-07-05 20:51:14 -04:00
wpferguson
ac81c95f7e Merge pull request #271 from Mark-64/ext_editor_add_doc
add documentation comments to ext_editor
2020-07-05 20:50:14 -04:00
wpferguson
48b915b0b2 Merge pull request #247 from noahwc/exportlut
Add an exporthaldclut lighttable module
2020-07-05 20:48:22 -04:00
Noah
593917db9a Fix gettext implementation 2020-07-04 23:20:30 -07:00
U-DESKTOP-HQME86J\marco
2d49b49bec add doc comments to ext_editor 2020-07-04 10:45:59 +02:00
U-DESKTOP-HQME86J\marco
323c8b6845 add doc comments 2020-07-04 10:27:06 +02:00
Bill Ferguson
e5ab30006b Added reset function. Changed namespace to img_time. 2020-06-24 21:51:40 -04:00
Bill Ferguson
14b97cdbd9 Created image_time.lua to adjust non-destructively adjust image time. 2020-06-21 17:14:36 -04:00
wpferguson
8f0f0a5298 Merge pull request #265 from wpferguson/correct_OpenInExplorer_shortcut
Fix OpenInExplorer shortcut function call
2020-06-21 16:37:27 -04:00
Bill Ferguson
497dbc8eaa Fixed function call arguments in shortcut. 2020-06-21 16:32:14 -04:00
wpferguson
11baf14280 Merge pull request #263 from wpferguson/preklov_OpenInExplorer_use_links
OpenInExplorer links option - This is @preklov's PR and he deserves the credit for the changes.  I just wasn't git ninja enough to figure out how to fix the problems, so I pulled it into a local branch and fixed it.
2020-06-20 15:02:42 -04:00
Bill Ferguson
948e8690dd Pulled preklov's branch, resolved conflicts, then merged into new branch with only 1 commit, essentially squashing the delete and replace file commits. 2020-06-20 14:49:31 -04:00
Noah
802c17d590 Added translated text 2020-06-16 01:59:43 -07:00
Noah
71e72b688f Exported luts now have folder hierarchy 2020-06-16 01:50:59 -07:00
supertobi
877b56ad67 Merge pull request #261 from scheckmedia/master
move function to create temp files to dtutils/file.lua
2020-06-15 14:08:03 +02:00
Tobias Scheck
87502fb17f move function to create temp files to dtutils/file.lua 2020-06-15 11:16:14 +02:00
supertobi
ed3e141601 Merge pull request #259 from GLLM/patch-1
Add the possibility to define a shortcut for OpenInExplorer LUA script
2020-06-15 10:33:30 +02:00
supertobi
f66d3ed3aa Merge pull request #236 from Mark-64/ext_editor_v4
Ext editor v4
2020-06-15 10:32:13 +02:00
supertobi
5488e45c78 Merge pull request #256 from scheckmedia/master
add photils plugin
2020-06-15 10:27:38 +02:00
Tobias Scheck
72bd66d3f5 add option to display confidence value, add notice that windows version is not available yet 2020-06-14 08:57:17 +02:00
Tobias Scheck
1378441dfa change labels to lower case to fit darktable style 2020-06-14 07:47:28 +02:00
GLLM
26d97b8cdf Update OpenInExplorer.lua
Add the possibility to define a shortcut for this LUA script.
2020-06-11 22:06:00 +02:00
Tobias Scheck
2e27b812d6 fix spelling 2020-06-10 14:48:27 +02:00
Tobias Scheck
fd97ac18da add comments for clarification; formatting 2020-06-10 14:48:18 +02:00
wpferguson
b9d14a2711 Merge pull request #257 from wpferguson/script_manager_sanitize
Bug Fix: script_manager can't process paths with lua special characters
2020-06-09 20:30:04 -04:00
Bill Ferguson
1c9042c496 Added a sanitize_lua function to the string library for sanitizing
strings of lua "magic" characters before using the string in a string
function that involves matching.

script_manager needs the path to the lua directory sanitized of lua
"magic" characters to prevent failure to extract the category and script
names.
2020-06-09 20:22:02 -04:00
Tobias Scheck
60fa4b9ee0 update description 2020-06-09 08:50:26 +02:00
Bill Ferguson
4c536b41bc checkpoint 2020-06-08 12:45:49 -04:00
Tobias Scheck
33f91af1af add photils plugin 2020-06-06 12:39:26 +02:00
wpferguson
05d86e89da Merge pull request #255 from wpferguson/update_README.md
Update README.md
2020-05-30 20:18:21 -04:00
Bill Ferguson
a02176fc63 Added instructions for using script_manager.lua as the luarc file. 2020-05-30 20:12:39 -04:00
wpferguson
55ced3ecc7 Merge pull request #251 from wpferguson/fix_script_manager_scrolling
Make script manager scrollable.
2020-05-23 17:01:52 -04:00
Bill Ferguson
4a81b560f9 Changed position of script manager to the center section of the left
panel at the bottom of the section.  This takes care of the problem
of darktable exceeding the vertical screen size and not being
scrollable.
2020-05-23 16:54:24 -04:00
wpferguson
6a7cd31ab4 Merge pull request #249 from wpferguson/fix_moduleExample
Update examples/moduleExample.lua
2020-05-23 16:18:07 -04:00
wpferguson
2515db31d8 Merge pull request #250 from sbolten/enhance_face_recognition
face_recognition.lua: Added "People" category to tag routine
2020-05-09 23:13:38 -04:00
Stephan Bolten
54a66f9ab2 Category Tag default & no_person_found
1 - empty by default
2 - not added when no_person_found
2020-05-09 19:58:57 +02:00
Bill Ferguson
0856858e73 Updated comments to reflect current usage practices. Added/updated links to the online manual for each of the widgets. Added translation. Removed reference to api_major_version 6 2020-05-08 00:15:31 -04:00
Noah Clarke
23131a301b Fix include path 2020-05-07 12:15:31 -07:00
Noah
aef7363bee make 8bit not 16bit luts (extra depth uneeded) 2020-05-06 13:19:12 -07:00
Stephan Bolten
d579b257a8 Added "People" category to tag routine
Added field in GUI to provide "People" category


Changed label of tag category


Changed Tooltip
2020-05-06 18:57:50 +02:00
Noah
4455347930 Exporting luts now creates a job 2020-05-06 01:14:34 -07:00
Noah
5709cfebd4 Remove unneeded API check 2020-05-05 00:02:47 -07:00
Noah
01184bee72 Updated API version check and forced PNG to 16 bits 2020-05-04 23:58:52 -07:00
wpferguson
8a8f6338c5 Merge pull request #243 from piterdias/master
Fixes face_recognition.lua failing to run in a fresh installation of darktable and adds some more logs
2020-04-27 15:05:14 -04:00
Piter Dias
71207e8f57 1 - face_recognition.lua fails to run in a fresh installation of darktable. It is due to pull request #221 where reset_preferences() and save_preferences() are called before being defined. This is actually a expected behavior because compiles script is one pass instead of two pass (like some other languages, like C). Moving a small piece of preferences setting code to the end of file solves the issue, because all required objects and functions are defined at that point.
2 - Add some more logs
2020-04-25 19:57:14 -03:00
supertobi
749f5b8e8f Merge pull request #240 from preklov/patch-2
German translation of OpenInExplorer.lua
2020-04-21 13:15:34 +02:00
wpferguson
88c1be09c2 Merge pull request #239 from preklov/master
Enabled macOS Finder in OpenInExplorer.lua
2020-04-20 13:55:51 -04:00
preklov
c1b59256be Add files via upload
The translation of the finished version of OpenInExplorer.lua
2020-04-20 16:14:32 +02:00
preklov
a3f57665a9 Delete OpenInExplorer.po 2020-04-20 16:13:05 +02:00
preklov
b8d29ca831 Add files via upload
The new and I hope the finished version for now.
2020-04-20 16:06:38 +02:00
preklov
90880ff4d1 Delete OpenInExplorer.lua 2020-04-20 16:03:25 +02:00
Noah
286ef77663 Some error checking and warnings 2020-04-19 00:36:47 -07:00
wpferguson
0bb8b72183 Merge pull request #241 from piterdias/face_recognition
Solve stability and performance issues on face_recognition.lua
2020-04-17 15:31:05 -04:00
Noah
28b9073010 Code and UI cleanup 2020-04-16 23:42:51 -07:00
Noah
fe97e29654 Working? version 2020-04-16 22:59:33 -07:00
Noah
792e782e8c basic frontend 2020-04-16 22:27:57 -07:00
Noah
33b99a97e4 initial module minus style picker 2020-04-16 17:51:09 -07:00
preklov
91ab85c825 Update OpenInExplorer.lua 2020-04-16 14:07:41 +02:00
Piter Dias
8c75b9316d Rollback some indentation changes 2020-04-16 01:46:45 -03:00
Piter Dias
9870ddbe86 face_recognition.lua script face some stability and performance issues on an i5-6600, 32 GB and GTX 1060 6GB:
1. Taking to much time – almost a day for 3000 images
    2. Crashing due to lines malformation in facerecognition.txt – some times face_recognition Python script concatenates two output lines in just once, what is not expected by face_recognition.lua script
    3. Crashing the whole system in Linux – I keep receiving “bash: fork: resource temporarily unavailable” trying to process a too big (starting from some hundreds) image list. Some other applications starts to crash at this point. It seems related to the attach tag call because commenting this instruction (for debugging purposes) eliminates the crashes.
After debugging the Lua script and checking Lua and SQL darktable out logs, I decided to make the following changes:
    1. Remove an inner loop in the processing results step – helps preventing issue 1
    2. Remove a loop in the export step – helps preventing issue 1
    3. Remove duplicate tags (when there are more more than a reference face for each person) while facerecognition.txt is read – helps preventing issue 1 and 3
    4. Make a sanity check with processing images, skip any malformation data – helps preventing issue 2
    5. Reduces the sqlite access by caching tags that where already created for a previous image – helps preventing issue 3

Those changes allowed may thousands (more than 30K) of images to be processed in half a day instead of several days.
2020-04-15 14:03:58 -03:00
supertobi
c7eb101920 Updated the "USAGE" section in the header of enfuseAdvanced.lua 2020-04-15 17:42:42 +02:00
preklov
2b171aef7c German translation of OpenInExplorer.lua 2020-04-14 19:33:54 +02:00
preklov
ec0fe0b047 Rename OpenInExplorer.lua to contrib/OpenInExplorer.lua 2020-04-14 16:15:45 +02:00
preklov
bbfa81f051 Add files via upload 2020-04-14 15:46:41 +02:00
preklov
bd5bef279c Delete OpenInExplorer.lua 2020-04-14 15:36:48 +02:00
preklov
afa95d5d34 Update OpenInExplorer.lua 2020-04-14 15:26:28 +02:00
preklov
fb331710e1 Add files via upload 2020-04-14 15:07:22 +02:00
preklov
224dce1b76 Delete OpenInExplorer.lua 2020-04-14 15:04:31 +02:00
wpferguson
a582f0229f Merge pull request #231 from Mark-64/RL_out_sharp
Add RL output sharpening script
2020-03-22 14:53:26 -04:00
U-DESKTOP-HQME86J\marco
a8e5a9b453 default no. of editors 2020-03-22 17:55:15 +01:00
U-DESKTOP-HQME86J\marco
2d69536489 fixes and code cleaning 2020-03-22 17:50:51 +01:00
U-DESKTOP-HQME86J\marco
ebd33c4c16 changed default sigma 2020-03-22 17:21:30 +01:00
wpferguson
3f5cf06bd9 Merge pull request #230 from Mark-64/fix_lib
Sanitize filepath before checking for its existance in Windows (fix #229)
2020-03-21 20:58:49 -04:00
wpferguson
1ec9cfb772 Merge pull request #234 from schwerdf/master
New script: 'transfer_hierarchy'.
2020-03-16 11:10:06 -04:00
August Schwerdfeger
050175802f Repositioned the 'transfer hierarchy' module below 'select' and 'selected image[s]'; added a translatability layer for strings; corrected behavior in the case of images with multiple versions. 2020-03-15 22:39:07 -05:00
August Schwerdfeger
19a1be1f74 Corrected a variable name. 2020-03-14 00:28:53 -05:00
August Schwerdfeger
093593dcef New script: 'transfer_hierarchy'. 2020-03-13 23:49:24 -05:00
U-DESKTOP-HQME86J\marco
51f28a386b requested modifications 2020-03-11 16:53:55 +01:00
U-DESKTOP-HQME86J\marco
4065bd5e11 test for GMIc executable 2020-03-10 21:21:41 +01:00
U-DESKTOP-HQME86J\marco
3e1746fc6e changed min API version 2020-03-10 20:34:13 +01:00
U-DESKTOP-HQME86J\marco
3b6dd856aa Add RL output sharpening script 2020-03-10 12:54:53 +01:00
U-DESKTOP-HQME86J\marco
635ea3f231 fix #229 2020-03-07 08:57:03 +01:00
wpferguson
48d1c62fed Merge pull request #227 from j3ffrw/master
enfuse output filename and blend colorspace
2020-03-02 11:55:27 -05:00
jefffermo@gmail.com
e97f2f3fc6 * make enfuse output file unique(fairly)
* add option --blend-colorspace of situation like this https://monochrome.sutic.nu/2019/06/18/enblend-4.2-color-space-blending-bug.html
2020-02-27 13:26:37 +08:00
supertobi
19b016a3dd Merge pull request #225 from Mark-64/ext_editor_v3
allow more than 9 editors
2020-02-09 20:14:25 +01:00
Marco
576e160372 Merge branch 'master' into ext_editor_v3 2020-02-07 21:26:47 +01:00
U-DESKTOP-HQME86J\marco
e586e0479d allow more that 9 editors 2020-02-07 21:13:19 +01:00
supertobi
0b5c866df6 Merge pull request #223 from wpferguson/remove_include_all
Remove include_all.lua
2020-01-29 09:23:17 +01:00
wpferguson
9828fb3934 Merge pull request #224 from wpferguson/readme_update
Update README.md
2020-01-28 14:16:52 -05:00
Bill Ferguson
430f443916 Added Stefan Klinger's darktable lua scripts repository 2020-01-28 14:14:47 -05:00
Bill Ferguson
d32e0eb5ab Removed include_all.lua since we now run cross platform and not all scripts run on all operating systems. 2020-01-28 12:11:33 -05:00
wpferguson
f72750576f Merge pull request #221 from piterdias/face_recognition_no_persons_found
Add "no persons found" entry and other enhancements
2020-01-27 17:51:52 -05:00
Piter Dias
e96b78b175 Add reset callback to face recognition widget and show the default values in the GUI. 2020-01-27 17:44:49 -03:00
Piter Dias
4708110804 1. Add "no persons found" entry for the situations that no persons are
detected in the photos
2. Check if the tags to applied are non empty and skip otherwise. It
applies to empty entries in the GUI ("unknown person tag" and "no
persons found"), when the user types something and then deletes it
3. Change the string "togs of images to ignore" to "tags of images to
ignore"
2020-01-23 21:12:49 -03:00
wpferguson
2725b82543 Merge pull request #220 from wpferguson/face_recognition_fix
Fix face_recognition crash
2020-01-22 13:18:01 -05:00
Bill Ferguson
1eafae6c6f Add check for WARNING lines complaining about files with no faces in the faces directory 2020-01-22 13:15:14 -05:00
wpferguson
11e25a21fa Merge pull request #218 from schwerdf/master
Correct argument escaping in 'dtutils_string.sanitize'.
2020-01-22 12:27:20 -05:00
August Schwerdfeger
51eda4e755 Made Windows- and POSIX-specific sanitization functions local; moved the 'sanitize' functions after the 'is_not_sanitized' functions that they call. 2020-01-21 22:17:12 -06:00
August Schwerdfeger
847c39749d Corrected escaping of command-line arguments in the 'sanitize' and 'is_not_sanitized' functions. 2020-01-21 00:29:18 -06:00
wpferguson
cb0975fa69 Merge pull request #217 from wpferguson/library_issues
Fix embedded quotes in strings not sanitized
2020-01-19 23:21:44 -05:00
Bill Ferguson
5b977733b3 Rewrote sanitize and is_not_sanitized to handle strings with embedded operatings system quotes. Fixes #216. 2020-01-19 23:11:41 -05:00
supertobi
c3d13a9890 Merge pull request #215 from Mark-64/error_fix
New version of ext-editor
2020-01-19 20:40:07 +01:00
U-DESKTOP-HQME86J\marco
bc4c04f795 error fix 2020-01-18 12:45:58 +01:00
wpferguson
32d5240e5d Merge pull request #214 from wpferguson/enfuse_bin_check
enfuse official - added binary found check
2020-01-05 16:57:24 -05:00
Bill Ferguson
64f423f3d1 Added a check to ensure the enfuse executable is found. If the executable isn't found, throw an error, print messgaes, and refuse to load. 2020-01-05 15:33:53 -05:00
supertobi
18d0e974e4 Merge pull request #211 from Yvan-56/patch-1
Update face_recognition for tolerance and locales
2020-01-04 19:51:15 +01:00
supertobi
c259f2dc72 Merge pull request #207 from Mark-64/ext_editor_v2
contrib/ext_editor version 2
2020-01-04 19:45:17 +01:00
Yvan-56
5223fdf4ae Update face_recognition for tolerance and locales
When locales use comma for numeric decimal separator, tolerance argument is not passed correctly.
I propose to switch temporally to "C" locales before converting float tolerance to string to get a period decimal separator.
2020-01-04 16:18:46 +01:00
Marco
a4132bd9bc Merge branch 'master' into ext_editor_v2 2020-01-04 12:22:24 +01:00
U-DESKTOP-HQME86J\marco
bf8d147ca5 requeste changes 2020-01-02 19:09:19 +01:00
U-DESKTOP-HQME86J\marco
5116d645db further improvements 2020-01-01 19:27:41 +01:00
U-DESKTOP-HQME86J\marco
a2850cac6b contrib/ext_editor v2 2019-12-30 14:07:42 +01:00
supertobi
ffed4efdec Merge pull request #204 from Mark-64/ext_editor
Add ext_editor.lua to contrib
2019-12-25 22:07:45 +01:00
U-DESKTOP-HQME86J\marco
cfa71927de requested updates 2019-12-25 16:16:44 +01:00
U-DESKTOP-HQME86J\marco
5d7a60206e requested updates 2019-12-25 16:02:53 +01:00
wpferguson
3556fef5ed Merge pull request #205 from wpferguson/dt3.0_readme_updates
Updated README.md to reflect latest changes
2019-12-24 11:54:37 -05:00
Bill Ferguson
252965a5d9 Updated README.md to reflect latest changes 2019-12-24 11:52:56 -05:00
Bill Ferguson
a7a25c0798 Merge branch 'wpferguson-remove_video_mencoder' 2019-12-24 11:43:39 -05:00
Bill Ferguson
944087649f removed deprecated contrib/video_mencoder.lua 2019-12-24 11:43:01 -05:00
wpferguson
69c9db6dda Merge pull request #203 from wpferguson/dt30examples
DT 3.0 lua API change demonstration
2019-12-24 11:37:28 -05:00
wpferguson
f5e86a8d25 Merge pull request #147 from simon04/delete-mo
Delete compiled .mo files
2019-12-24 11:37:02 -05:00
U-DESKTOP-HQME86J\marco
6a89357b54 compatibility with Linux 2019-12-23 11:34:01 +01:00
U-DESKTOP-HQME86J\marco
64c8f8fc3e added darkroom mode 2019-12-22 21:08:32 +01:00
U-DESKTOP-HQME86J\marco
54ff56b021 added copy and edit button + code cleaning 2019-12-21 21:12:46 +01:00
U-DESKTOP-HQME86J\marco
3d3ad89fba Add ext_editor.lua to contrib 2019-12-20 23:00:57 +01:00
Bill Ferguson
7ee140c8c8 created 3 demonstration scripts of the dt3.0 lua API additions 2019-12-19 13:38:25 -05:00
Simon Legner
11607e7e7d Delete compiled .mo files 2018-09-18 10:26:40 +02:00
125 changed files with 13871 additions and 2198 deletions

111
ChangeLog.md Normal file
View File

@@ -0,0 +1,111 @@
## Changes from most recent to oldest
**25 Mar 2023 - wpferguson**
* Added script_manager darktable shortcut integration
* Moved filename/path/extension string functions to string library
**06 Mar 2023 - wpferguson - Added option to disable script_manager update check**
**12 Jan 2022 - wpferguson - Documented contrib/rename_images.lua**
**28 Dec 2021 - wpferguson - Replaced deprecated _which_ command with _command -v_ in lib/dtutils/file.lua**
**12 Dec 2021 - wpferguson**
* Added deprecated function to library
* Added deprecation warning to contrib/rename-tags.lua
**22 Oct 2021 - wpferguson for Volker Bödker - make sure sequence is 4 digits in rename_images.lua**
**31 Aug 2021 - wpferguson - remove styles hiding from AutoGrouper.lua**
**02 Jul 2021 - wpferguson - merged API-7.0.0-dev branch to master**
* API-7.0.0 is darktable 3.6
* breaking changes
* register_event argments changed
* register_action arguments changed
* register_selection arguments changed
* register_event arguments changed
* scripts updated to API-7.0.0 compatibility
* script_manger updated to be API aware and check out the proper branch
based on darktable API version
**01 Jul 2021 - wpferguson - created branch for API-6.1.0 - darktable 3.4**
**20 Jun 2021 - wpferguson - created branches for older API versions**
* API-6.0.0 - darktable 3.2.1
* API-5.0.2 - darktable 3.0
* API-5.0.1 - darktable 2.6.1
* API-5.0.0 - darktable 2.4
* API-4.0.0 - darktable 2.2
* API-3.0.0 - darktable 2.0
**19 Jun 2021 - wpferguson - fix issue 312, image_path_in_ui**
**02 Jun 2021 - wpferguson - fix contrib/quicktag**
* set new entry field is_password to false so entry
is visible to user while typing.
**19 Mar 2021 - wpferguson - fixed crash in contrib/HDRmerge.lua**
* Made generated filename routine gracefully handle names that
are not in the expected format.
**15 Mar 2021 - scheckmedia - updated contrib/photils.lua**
* refactor print method
* add option to apply selected tags from a single image to multiple images
* add setting parameter to enable/disable the export of an image before tag suggestion
**25 Feb 2021 - wpferguson - added detached mode to contrib/gimp.lua**
* Added run_detached checkbox to the exporter GUI. Selecting run_detached
let's GIMP keep running and accepting additional images. It does not return
the edited images to darktable.
**24 Feb 2021 - Mark64 - make ext_editor lib visible in darkroom view**
**17 Feb 2021 - wpferguson - API 6.2.3 register_action changes**
* Added check for API version and supplied a name argument if the
API version was greater than or equal to 6.2.3
**10 Feb 2021 - wpferguson - bugfix select_untagged**
* Fixed callback to return a list of images as expected instead of
doing the selection in the callback
**10 Feb 2021 - wpferguson - bugfix API 6.2.1 compatibility**
* The inline check for API version didn't handle argument return
correctly so added a transition library with a register_event function
override to check the API version and process the arguments correctly.
**9 Feb 2021 - wpferguson - bugfix API 6.2.2 compatibility**
* The inline check for API version didn't handle argument return
correctly so changed it to a full if/else block
**4 Reb 2021 - wpferguson - API 6.2.2 compatibililty**
* Added check for API version and supplied a name argument to register_selection
if the API version was greater than or eqal to 6.2.2
**1 Feb 2021 - wpferguson - API 6.2.1 compatibility**
* Added check for API version and supplied a name argument to register_event
if the API version was greater than or eqal to 6.2.1
**21 Jan 2021 - wpferguson - Modified dtutils function find_image_by_id**
* For users with API 6.2.0 or greater - Enabled use of new API function
darktable.database_get_image() in find_image_by_id().
**19 Jan 2021 - schwerdf - Added dtutils library function find_image_by_id()**
* Added new library function to retrieve an image from the library based on it's ID instead
of it's row number in the database
**10 Jan 2021 - chrisaga - copy_attach_detach_tags localization**
**7 Jan 2021 - dtorop - add contrib/fujifilm_dynamic_range**
* add a new contrib script, fujifilm_dynamic_range to adjust exposure
based on the exposure bias camera setting

182
README.md
View File

@@ -7,23 +7,25 @@ efforts of the darktable developers, maintainers, contributors and community. Th
contained in the repository, whether they can be run by themselves (Standalone - Yes) or depend on other contained in the repository, whether they can be run by themselves (Standalone - Yes) or depend on other
scripts (Standalone - No), what operating systems they are known to work on (L - Linux, M - MacOS, W - Windows), and their purpose. scripts (Standalone - No), what operating systems they are known to work on (L - Linux, M - MacOS, W - Windows), and their purpose.
For the latest changes, see the [ChangeLog](ChangeLog.md)
### Official Scripts ### Official Scripts
These scripts are written primarily by the darktable developers and maintained by the authors and/or repository maintainers. They are located in the official/ directory. These scripts are written primarily by the darktable developers and maintained by the authors and/or repository maintainers. They are located in the official/ directory.
Name|Standalone|OS |Purpose Name|Standalone|OS |Purpose
----|:--------:|:---:|------- ----|:--------:|:---:|-------
check_for_updates|Yes|LMW|Check for updates to darktable [check_for_updates](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/check_for_updates)|Yes|LMW|Check for updates to darktable
copy_paste_metadata|Yes|LMW|Copy and paste metadata, tags, ratings, and color labels between images [copy_paste_metadata](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/copy_paste_metadata)|Yes|LMW|Copy and paste metadata, tags, ratings, and color labels between images
delete_long_tags|Yes|LMW|Delete all tags longer than a specified length [delete_long_tags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/delete_long_tags)|Yes|LMW|Delete all tags longer than a specified length
delete_unused_tags|Yes|LMW|Delete tags that have no associated images [delete_unused_tags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/delete_unused_tags)|Yes|LMW|Delete tags that have no associated images
enfuse|No|L|Exposure blend several images (HDR) [enfuse](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/enfuse)|No|L|Exposure blend several images (HDR)
generate_image_txt|No|L|Generate txt sidecar files to be overlaid on zoomed images [generate_image_txt](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/generate_image_txt)|No|L|Generate txt sidecar files to be overlaid on zoomed images
image_path_in_ui|Yes|LMW|Plugin to display selected image path [image_path_in_ui](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/image_path_in_ui)|Yes|LMW|Plugin to display selected image path
import_filter_manager|Yes|LMW|Manager for import filters [import_filter_manager](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/import_filter_manager)|Yes|LMW|Manager for import filters
import_filters|No|LMW|Two import filters for use with import_filter_manager [import_filters](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/import_filters)|No|LMW|Two import filters for use with import_filter_manager
save_selection|Yes|LMW|Provide save and restore from multiple selection buffers [save_selection](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/save_selection)|Yes|LMW|Provide save and restore from multiple selection buffers
selection_to_pdf|No|L|Generate a PDF file from the selected images [selection_to_pdf](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/selection_to_pdf)|No|L|Generate a PDF file from the selected images
### Contributed Scripts ### Contributed Scripts
@@ -32,34 +34,43 @@ These scripts are contributed by users. They are meant to have an "owner", i.e.
Name|Standalone|OS |Purpose Name|Standalone|OS |Purpose
----|:--------:|:---:|------- ----|:--------:|:---:|-------
AutoGrouper|Yes|LMW|Group images together by time [AutoGrouper](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/autogrouper)|Yes|LMW|Group images together by time
autostyle|Yes|LMW|Automatically apply styles on import [autostyle](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/autostyle)|Yes|LMW|Automatically apply styles on import
clear_GPS|Yes|LMW|Reset GPS information for selected images change_group_leader|Yes|LMW|Change which image leads group
CollectHelper|Yes|LMW|Add buttons to selected images module to manipulate the collection [clear_GPS](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/clear_gps)|Yes|LMW|Reset GPS information for selected images
copy_attach_detach_tags|Yes|LMW|Copy and paste tags from/to images [CollectHelper](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/collecthelper)|Yes|LMW|Add buttons to selected images module to manipulate the collection
cr2hdr|Yes|L|Process image created with Magic Lantern Dual ISO [copy_attach_detach_tags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/copy_attach_detach_tags)|Yes|LMW|Copy and paste tags from/to images
enfuseAdvanced|No|LMW|Merge multiple images into Dynamic Range Increase (DRI) or Depth From Focus (DFF) images [cr2hdr](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/cr2hdr)|Yes|L|Process image created with Magic Lantern Dual ISO
face_recognition|No|LM|Identify and tag images using facial recognition [enfuseAdvanced](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/enfuseadvanced)|No|LMW|Merge multiple images into Dynamic Range Increase (DRI) or Depth From Focus (DFF) images
fujifilm_ratings|No|LM|Support importing Fujifilm ratings [exportLUT](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/exportlut)|Yes|LMW|Create a LUT from a style and export it
geoJSON_export|No|L|Create a geo JSON script with thumbnails for use in ... [ext_editor](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/ext_editor)|No|LW|Export pictures to collection and edit them with up to nine user-defined external editors
geoToolbox|No|LMW|A toolbox of geo functions [face_recognition](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/face_recognition)|No|LM|Identify and tag images using facial recognition
gimp|No|LMW|Open an image in GIMP for editing and return the result [fujifilm_dynamic_range](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/fujifilm_dynamic_range)|No|LMW|Correct fujifilm exposure based on exposure bias camera setting
gpx_export|No|LMW|Export a GPX track file from selected images GPS data [fujifilm_ratings](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/fujifilm_ratings)|No|LM|Support importing Fujifilm ratings
HDRMerge|No|LMW|Combind the selected images into an HDR DNG and return the result [geoJSON_export](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/geojson_export)|No|L|Create a geo JSON script with thumbnails for use in ...
hugin|No|LMW|Combine selected images into a panorama and return the result [geoToolbox](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/geotoolbox)|No|LMW|A toolbox of geo functions
image_stack|No|LMW|Combine a stack of images to remove noise or transient objects [gimp](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/gimp)|No|LMW|Open an image in GIMP for editing and return the result
kml_export|No|L|Export photos with a KML file for usage in Google Earth [gpx_export](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/gpx_export)|No|LMW|Export a GPX track file from selected images GPS data
LabelsToTags|Yes|LMW|Apply tags based on color labels and ratings [HDRMerge](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/hdrmerge)|No|LMW|Combine the selected images into an HDR DNG and return the result
OpenInExplorer|No|LMW|Open the selected images in the system file manager [hugin](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/hugin)|No|LMW|Combine selected images into a panorama and return the result
passport_guide|Yes|LMW|Add passport cropping guide to darkroom crop tool [image_stack](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/image_stack)|No|LMW|Combine a stack of images to remove noise or transient objects
pdf_slideshow|No|LM|Export images to a PDF slideshow [image_time](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/image_time)|Yes|LMW|Adjust the EXIF image time
quicktag|Yes|LMW|Create shortcuts for quickly applying tags [kml_export](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/kml_export)|No|L|Export photos with a KML file for usage in Google Earth
rate_group|Yes|LMW|Apply or remove a star rating from grouped images [LabelsToTags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/labelstotags)|Yes|LMW|Apply tags based on color labels and ratings
rename-tags|Yes|LMW|Change a tag name [OpenInExplorer](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/openinexplorer)|No|LMW|Open the selected images in the system file manager
select_untagged|Yes|LMW|Enable selection of untagged images [passport_guide](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/passport_guide)|Yes|LMW|Add passport cropping guide to darkroom crop tool
slideshowMusic|No|L|Play music during a slideshow [pdf_slideshow](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/pdf_slideshow)|No|LM|Export images to a PDF slideshow
video_ffmpeg|No|LMW|Export video from darktable [photils](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/photils)|No|LM|Automatic tag suggestions for your images
video_mencoder|No|L|Export video from darktable [quicktag](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/quicktag)|Yes|LMW|Create shortcuts for quickly applying tags
[rate_group](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/rate_group)|Yes|LMW|Apply or remove a star rating from grouped images
[rename_images](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/rename_images)|Yes|LMW|Rename single or multiple images
[rename-tags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/rename-tags)|Yes|LMW|Change a tag name
[RL_out_sharp](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/rl_out_sharp)|No|LW|Output sharpening using GMic (Richardson-Lucy algorithm)
[select_non_existing](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/select_non_existing)|Yes|LMW|Enable selection of non-existing images in the the currently worked on images
[select_untagged](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/select_untagged)|Yes|LMW|Enable selection of untagged images
[slideshowMusic](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/slideshowMusic)|No|L|Play music during a slideshow
[transfer_hierarchy](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/transfer_hierarchy)|Yes|LMW|Image move/copy preserving directory hierarchy
[video_ffmpeg](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/video_ffmpeg)|No|LMW|Export video from darktable
### Example Scripts ### Example Scripts
@@ -67,14 +78,17 @@ These scripts provide examples of how to use specific portions of the API. They
Name|Standalone|OS |Purpose Name|Standalone|OS |Purpose
----|:--------:|:---:|------- ----|:--------:|:---:|-------
api_version|Yes|LMW|Print the current API version [api_version](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/api_version)|Yes|LMW|Print the current API version
gettextExample|Yes|LM|How to use translation [darkroom_demo](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/darkroom_demo)|Yes|LMW|Demonstrate changing images in darkoom
hello_world|Yes|LMW|Prints hello world when darktable starts [gettextExample](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/gettextexample)|Yes|LM|How to use translation
moduleExample|Yes|LMW|How to create a lighttable module [hello_world](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/hello_world)|Yes|LMW|Prints hello world when darktable starts
multi_os|No|LMW|How to create a cross platform script that calls an external executable [lighttable_demo](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/lighttable_demo)|Yes|LMW|Demonstrate controlling lighttable mode, zoom, sorting and filtering
preferenceExamples|Yes|LMW|How to use preferences in a script [moduleExample](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/moduleexample)|Yes|LMW|How to create a lighttable module
printExamples|Yes|LMW|How to use various print functions from a script [multi_os](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/multi_os)|No|LMW|How to create a cross platform script that calls an external executable
running_os|Yes|LMW|Print out the running operating system [panels_demo](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/panels_demo)|Yes|LMW|Demonstrate hiding and showing darktable panels
[preferenceExamples](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/preferenceexamples)|Yes|LMW|How to use preferences in a script
[printExamples](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/printexamples)|Yes|LMW|How to use various print functions from a script
[running_os](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/running_os)|Yes|LMW|Print out the running operating system
### Tools ### Tools
@@ -82,11 +96,11 @@ Tool scripts perform functions relating to the repository, such as generating do
Name|Standalone|OS |Purpose Name|Standalone|OS |Purpose
----|:--------:|:---:|------- ----|:--------:|:---:|-------
executable_manager|Yes|LMW|Manage the external executables used by the lua scripts [executable_manager](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/executable_manager)|Yes|LMW|Manage the external executables used by the lua scripts
gen_i18n_mo|No|LMW|Generate compiled translation files (.mo) from source files (.po) [gen_i18n_mo](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/gen_i18n_mo)|No|LMW|Generate compiled translation files (.mo) from source files (.po)
get_lib_manpages|No|LM|Retrieve the library documentation and output it in man page and PDF format [get_lib_manpages](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/get_lib_manpages)|No|LM|Retrieve the library documentation and output it in man page and PDF format
get_libdoc|No|LMW|Retrieve the library documentation and output it as text [get_libdoc](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/get_libdoc)|No|LMW|Retrieve the library documentation and output it as text
script_manager|No|LMW|Manage (install, update, enable, disable) the lua scripts [script_manager](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/script_manager)|No|LMW|Manage (install, update, enable, disable) the lua scripts
### Related third-party projects ### Related third-party projects
@@ -102,6 +116,8 @@ The following third-party projects are listed for information only. Think of thi
* [johnnyrun/darktable_lua_gimp](https://github.com/johnnyrun/darktable_lua_gimp) – GIMP export * [johnnyrun/darktable_lua_gimp](https://github.com/johnnyrun/darktable_lua_gimp) – GIMP export
* [arru/darktable-scripts](https://github.com/arru/darktable-scripts) * [arru/darktable-scripts](https://github.com/arru/darktable-scripts)
* [nbremond77/darktable](https://github.com/nbremond77/darktable/tree/master/scripts) * [nbremond77/darktable](https://github.com/nbremond77/darktable/tree/master/scripts)
* [s5k6/dtscripts](https://github.com/s5k6/dtscripts)
* [ChristianBirzer/darktable_extra_scripts](https://github.com/ChristianBirzer/darktable_extra_scripts)
## Download and Install ## Download and Install
@@ -110,14 +126,24 @@ are met as well as providing an easy update path. Single scripts listed as stand
### snap packages ### snap packages
The snap version of darktable comes with lua included starting with version 2.4.3snap2. It is currently in the edge channel, but should reach the stable channel soon. The snap version of darktable comes with lua included starting with version 2.4.3snap2.
Ensure git is installed on your system. If it isn't, use the package manager to install it. Then open a terminal and: Ensure git is installed on your system. If it isn't, use the package manager to install it. Then open a terminal and:
cd ~/snap/darktable/current cd ~/snap/darktable/current
git clone https://github.com/darktable-org/lua-scripts.git lua git clone https://github.com/darktable-org/lua-scripts.git lua
### flatpak and appimage packages ### flatpak packages
Flatpak packages now use the internal lua interpreter.
Ensure git is installed on your system. If it isn't, use the package manager to install it. Then open a terminal and:
cd ~/.var/app/org.darktable.Darktable/config/darktable
git clone https://github.com/darktable-org/lua-scripts.git lua
### appimage packages
These packages run in their own environment and don't have access to a lua interpreter, therefore the scripts can't run. The packagers could enable the internal interpreter, or allow the package to link the interpreter from the operating system, or bundle a copy of lua with the package. If you use one of these packages and wish to use the lua scripts, please contact the package maintainer and suggest the above fixes. These packages run in their own environment and don't have access to a lua interpreter, therefore the scripts can't run. The packagers could enable the internal interpreter, or allow the package to link the interpreter from the operating system, or bundle a copy of lua with the package. If you use one of these packages and wish to use the lua scripts, please contact the package maintainer and suggest the above fixes.
@@ -132,8 +158,6 @@ Ensure git is installed on your system. If it isn't, use the package manager to
Ensure git is installed on your system. Git can be obtained from https://gitforwindows.org/, as well as other places. If you use the gitforwindows.org distribution, install the Git Bash Shell also as it will aid in debugging the scripts if necessary. Then open a command prompt and run: Ensure git is installed on your system. Git can be obtained from https://gitforwindows.org/, as well as other places. If you use the gitforwindows.org distribution, install the Git Bash Shell also as it will aid in debugging the scripts if necessary. Then open a command prompt and run:
Open a command prompt.
cd %LOCALAPPDATA%\darktable cd %LOCALAPPDATA%\darktable
git clone https://github.com/darktable-org/lua-scripts.git lua git clone https://github.com/darktable-org/lua-scripts.git lua
@@ -143,6 +167,24 @@ If you don't have %LOCALAPPDATA%\darktable you have to start dartable at least o
When darktable starts it looks for a file name `~/.config/darktable/luarc` (`%LOCALAPPDATA%\darktable\luarc` for windows) and reads it to see which scripts to include. The file is a plain text file with entries of the form `require "<directory>/<name>"` where directory is the directory containing the scripts, from the above list, and name is the name from the above list. To include GIMP the line would be `require "contrib/gimp"`. When darktable starts it looks for a file name `~/.config/darktable/luarc` (`%LOCALAPPDATA%\darktable\luarc` for windows) and reads it to see which scripts to include. The file is a plain text file with entries of the form `require "<directory>/<name>"` where directory is the directory containing the scripts, from the above list, and name is the name from the above list. To include GIMP the line would be `require "contrib/gimp"`.
The recommended way to enable and disable specific scripts is using the script manager module. To use script manager do the following:
### Linux or MacOS
echo 'require "tools/script_manager"' > ~/.config/darktable/luarc
### Windows
echo require "tools/script_manager" > %LOCALAPPDATA%\darktable\luarc
### Snap
echo 'require "tools/script_manager"' > ~/snap/darktable/current/luarc
### Flatpak
echo 'require "tools/script_manager"' > ~/.var/app/org.darktable.Darktable/config/darktable/luarc
You can also create or add lines to the luarc file from the command line: You can also create or add lines to the luarc file from the command line:
`echo 'require "contrib/gimp"' > ~/.config/darktable/luarc` to create the file with a gimp entry\ `echo 'require "contrib/gimp"' > ~/.config/darktable/luarc` to create the file with a gimp entry\
@@ -166,6 +208,12 @@ To update the script repository, open a terminal or command prompt and do the fo
cd ~/snap/darktable/current/lua cd ~/snap/darktable/current/lua
git pull git pull
### Flatpak
cd ~/.var/app/org.darktable.Darktable/config/darktable/lua
git pull
### Linux and MacOS ### Linux and MacOS
cd ~/.config/darktable/lua/ cd ~/.config/darktable/lua/
@@ -178,15 +226,19 @@ To update the script repository, open a terminal or command prompt and do the fo
## Documentation ## Documentation
Each script includes its own documentation and usage in its header, please refer to them. The [Lua Scripts Manual](https://darktable-org.github.io/luadocs/lua.scripts.manual/) provides documentation
for the scripts transcribed from the header comments. Each script also contains comments and usage instructions
in the header comments.
Lua-script libraries documentation may be generated using the tools in the tools/ directory. The [Lua Scripts Library API Manual](https://darktable-org.github.io/luadocs/lua.scripts.api.manual/) provides
documentation of the libraries and functions. Lua-script libraries documentation may also be generated using
the tools in the tools/ directory.
More information about the scripting with Lua can be found in the darktable user manual: More information about the scripting with Lua can be found in the darktable user manual:
https://www.darktable.org/usermanual/en/lua_chapter.html [Scripting with Lua](https://darktable-org.github.io/dtdocs/lua/)
The darktable Lua API documentation is here: The [Lua API Manual](https://darktable-org.github.io/luadocs/lua.api.manual/) provides docuemntation of the
https://www.darktable.org/lua-api/ darktable Lua API.
## Troubleshooting ## Troubleshooting
@@ -196,13 +248,17 @@ Running darktable with Lua debugging enabled provides more information about wha
Open a terminal and start darktable with the command `snap run darktable -d lua`. This provides debugging information to give you insight into what is happening. Open a terminal and start darktable with the command `snap run darktable -d lua`. This provides debugging information to give you insight into what is happening.
### Linux and MacOS ### Linux
Open a terminal and start darktable with the command `darktable -d lua`. This provides debugging information to give you insight into what is happening. Open a terminal and start darktable with the command `darktable -d lua`. This provides debugging information to give you insight into what is happening.
### MacOS
Open a terminal and start darktable with the command `/Applications/darktable.app/Contents/MacOS/darktable -d lua`. This provides debugging information to give you insight into what is happening.
### Windows ### Windows
Open the Git Bash Shell. Start darktable with the command `/c/Program\ Files/darktable/bin/darktable -d lua`. This provides debugging information to give you insight into what is happening. Open a command prompt. Start darktable with the command "C:\Program Files\darktable\bin\darktable" -d lua > log.txt. This provides debugging information to give you insight into what is happening.
## Contributing ## Contributing

View File

@@ -37,122 +37,183 @@ selected images, the other button performs grouping on the entire active collect
]] ]]
local dt = require "darktable" local dt = require "darktable"
local du = require "lib/dtutils"
du.check_min_api_version("7.0.0", "AutoGrouper")
local MOD = 'autogrouper' local MOD = 'autogrouper'
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local gettext = dt.gettext local gettext = dt.gettext
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("AutoGrouper",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("AutoGrouper",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid) local function _(msgid)
return gettext.dgettext("AutoGrouper", msgid) return gettext.dgettext("AutoGrouper", msgid)
end end
local Ag = {}
Ag.module_installed = false
Ag.event_registered = false
local GUI = {
gap = {},
selected = {},
collection = {}
}
local function InRange(test, low, high) --tests if test value is within range of low and high (inclusive) local function InRange(test, low, high) --tests if test value is within range of low and high (inclusive)
if test >= low and test <= high then if test >= low and test <= high then
return true return true
else else
return false return false
end end
end end
local function CompTime(first, second) --compares the timestamps and returns true if first was taken before second local function CompTime(first, second) --compares the timestamps and returns true if first was taken before second
first_time = first.exif_datetime_taken first_time = first.exif_datetime_taken
if string.match(first_time, '[0-9]') == nil then first_time = '9999:99:99 99:99:99' end if string.match(first_time, '[0-9]') == nil then first_time = '9999:99:99 99:99:99' end
first_time = tonumber(string.gsub(first_time, '[^0-9]*','')) first_time = tonumber(string.gsub(first_time, '[^0-9]*',''))
second_time = second.exif_datetime_taken second_time = second.exif_datetime_taken
if string.match(second_time, '[0-9]') == nil then second_time = '9999:99:99 99:99:99' end if string.match(second_time, '[0-9]') == nil then second_time = '9999:99:99 99:99:99' end
second_time = tonumber(string.gsub(second_time, '[^0-9]*','')) second_time = tonumber(string.gsub(second_time, '[^0-9]*',''))
return first_time < second_time return first_time < second_time
end end
local function SeperateTime(str) --seperates the timestamp into individual components for used with OS.time operations local function SeperateTime(str) --seperates the timestamp into individual components for used with OS.time operations
local cleaned = string.gsub(str, '[^%d]',':') local cleaned = string.gsub(str, '[^%d]',':')
cleaned = string.gsub(cleaned, '::*',':') --YYYY:MM:DD:hh:mm:ss cleaned = string.gsub(cleaned, '::*',':') --YYYY:MM:DD:hh:mm:ss
local year = string.sub(cleaned,1,4) local year = string.sub(cleaned,1,4)
local month = string.sub(cleaned,6,7) local month = string.sub(cleaned,6,7)
local day = string.sub(cleaned,9,10) local day = string.sub(cleaned,9,10)
local hour = string.sub(cleaned,12,13) local hour = string.sub(cleaned,12,13)
local min = string.sub(cleaned,15,16) local min = string.sub(cleaned,15,16)
local sec = string.sub(cleaned,18,19) local sec = string.sub(cleaned,18,19)
return {year = year, month = month, day = day, hour = hour, min = min, sec = sec} return {year = year, month = month, day = day, hour = hour, min = min, sec = sec}
end end
local function GetTimeDiff(curr_image, prev_image) --returns the time difference (in sec.) from current image and the previous image local function GetTimeDiff(curr_image, prev_image) --returns the time difference (in sec.) from current image and the previous image
local curr_time = SeperateTime(curr_image.exif_datetime_taken) local curr_time = SeperateTime(curr_image.exif_datetime_taken)
local prev_time = SeperateTime(prev_image.exif_datetime_taken) local prev_time = SeperateTime(prev_image.exif_datetime_taken)
return os.time(curr_time)-os.time(prev_time) return os.time(curr_time)-os.time(prev_time)
end end
local function main(on_collection) local function main(on_collection)
local images = {} local images = {}
if on_collection then if on_collection then
local col_images = dt.collection local col_images = dt.collection
for i,image in ipairs(col_images) do --copy images to a standard table, table.sort barfs on type dt_lua_singleton_image_collection for i,image in ipairs(col_images) do --copy images to a standard table, table.sort barfs on type dt_lua_singleton_image_collection
table.insert(images,i,image) table.insert(images,i,image)
end
else
images = dt.gui.selection()
end end
dt.preferences.write(MOD, 'active_gap', 'integer', GUI.gap.value) else
if #images < 2 then images = dt.gui.selection()
dt.print('please select at least 2 images') end
return dt.preferences.write(MOD, 'active_gap', 'integer', GUI.gap.value)
end if #images < 2 then
table.sort(images, function(first, second) return CompTime(first,second) end) --sort images by timestamp dt.print('please select at least 2 images')
return
for i, image in ipairs(images) do end
if i == 1 then table.sort(images, function(first, second) return CompTime(first,second) end) --sort images by timestamp
prev_image = image
elseif string.match(image.exif_datetime_taken, '[%d]') ~= nil then --make sure current image has a timestamp, if so check if it is within the user specified gap value and add to group for i, image in ipairs(images) do
local curr_image = image if i == 1 then
if GetTimeDiff(curr_image, prev_image) <= GUI.gap.value then prev_image = image
images[i]:group_with(images[i-1]) elseif string.match(image.exif_datetime_taken, '[%d]') ~= nil then --make sure current image has a timestamp, if so check if it is within the user specified gap value and add to group
end local curr_image = image
prev_image = curr_image if GetTimeDiff(curr_image, prev_image) <= GUI.gap.value then
end images[i]:group_with(images[i-1])
end
prev_image = curr_image
end end
end
end end
-- GUI -- local function install_module()
GUI = { if not Ag.module_installed then
gap = {}, dt.print_log("installing module")
selected = {}, dt.register_lib(
collection = {} MOD, -- Module name
} _('auto group'), -- name
temp = dt.preferences.read(MOD, 'active_gap', 'integer') true, -- expandable
if not InRange(temp, 1, 86400) then temp = 3 end true, -- resetable
GUI.gap = dt.new_widget('slider'){ {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 99}}, -- containers
label = _('group gap [sec.]'), dt.new_widget("box"){
tooltip = _('minimum gap, in seconds, between groups'),
soft_min = 1,
soft_max = 60,
hard_min = 1,
hard_max = 86400,
step = 1,
digits = 0,
value = temp,
reset_callback = function(self)
self.value = 3
end
}
GUI.selected = dt.new_widget("button"){
label = _('auto group: selected'),
tooltip =_('auto group selected images'),
clicked_callback = function() main(false) end
}
GUI.collection = dt.new_widget("button"){
label = _('auto group: collection'),
tooltip =_('auto group the entire collection'),
clicked_callback = function() main(true) end
}
dt.register_lib(
'AutoGroup_Lib', -- Module name
_('auto group'), -- name
true, -- expandable
true, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 99}}, -- containers
dt.new_widget("box"){
orientation = "vertical", orientation = "vertical",
GUI.gap, GUI.gap,
GUI.selected, GUI.selected,
GUI.collection GUI.collection
} }
) )
Ag.module_installed = true
dt.print_log("module installed")
dt.print_log("styles module visibility is " .. tostring(dt.gui.libs["styles"].visible))
end
end
local function destroy()
dt.gui.libs[MOD].visible = false
end
local function restart()
dt.gui.libs[MOD].visible = true
end
-- GUI --
temp = dt.preferences.read(MOD, 'active_gap', 'integer')
if not InRange(temp, 1, 86400) then temp = 3 end
GUI.gap = dt.new_widget('slider'){
label = _('group gap [sec.]'),
tooltip = _('minimum gap, in seconds, between groups'),
soft_min = 1,
soft_max = 60,
hard_min = 1,
hard_max = 86400,
step = 1,
digits = 0,
value = temp,
reset_callback = function(self)
self.value = 3
end
}
GUI.selected = dt.new_widget("button"){
label = _('auto group: selected'),
tooltip =_('auto group selected images'),
clicked_callback = function() main(false) end
}
GUI.collection = dt.new_widget("button"){
label = _('auto group: collection'),
tooltip =_('auto group the entire collection'),
clicked_callback = function() main(true) end
}
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not Ag.event_registered then
dt.register_event(
"AutoGrouper", "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
Ag.event_registered = true
end
end
script_data.destroy = destroy
script_data.destroy_method = "hide"
script_data.restart = restart
script_data.show = restart
return script_data

View File

@@ -46,6 +46,7 @@ In the "Selected Images" module click on "Collect on this Image"
]] ]]
local dt = require "darktable" local dt = require "darktable"
local du = require "lib/dtutils"
local gettext = dt.gettext local gettext = dt.gettext
local previous = nil local previous = nil
local all_active = false local all_active = false
@@ -53,6 +54,17 @@ local all_active = false
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("CollectHelper",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("CollectHelper",dt.configuration.config_dir.."/lua/locale/")
du.check_min_api_version("7.0.0", "CollectHelper")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local function _(msgid) local function _(msgid)
return gettext.dgettext("CollectHelper", msgid) return gettext.dgettext("CollectHelper", msgid)
end end
@@ -65,6 +77,7 @@ local function CheckSingleImage(selection)
end end
return false return false
end end
local function CheckHasColorLabel(selection) local function CheckHasColorLabel(selection)
local ret = false local ret = false
for _,image in pairs(selection) do for _,image in pairs(selection) do
@@ -81,6 +94,7 @@ local function PreviousCollection()
previous = dt.gui.libs.collect.filter(previous) previous = dt.gui.libs.collect.filter(previous)
end end
end end
local function CollectOnFolder(all_rules, all_active) local function CollectOnFolder(all_rules, all_active)
local images = dt.gui.selection() local images = dt.gui.selection()
if CheckSingleImage(images) then if CheckSingleImage(images) then
@@ -101,6 +115,7 @@ local function CollectOnFolder(all_rules, all_active)
previous = dt.gui.libs.collect.filter(rules) previous = dt.gui.libs.collect.filter(rules)
end end
end end
local function CollectOnColors(all_rules, all_active) local function CollectOnColors(all_rules, all_active)
local images = dt.gui.selection() local images = dt.gui.selection()
if CheckSingleImage(images) then if CheckSingleImage(images) then
@@ -157,6 +172,7 @@ local function CollectOnColors(all_rules, all_active)
end end
end end
end end
local function CollectOnAll_AND() local function CollectOnAll_AND()
local images = dt.gui.selection() local images = dt.gui.selection()
if CheckSingleImage(images) then if CheckSingleImage(images) then
@@ -172,29 +188,42 @@ local function CollectOnAll_AND()
previous = dt.gui.libs.collect.filter(rules) previous = dt.gui.libs.collect.filter(rules)
end end
local function destroy()
dt.gui.libs.image.destroy_action("CollectHelper_prev")
if dt.preferences.read('module_CollectHelper','folder','bool') then
dt.gui.libs.image.destroy_action("CollectHelper_folder")
end
if dt.preferences.read('module_CollectHelper','colors','bool') then
dt.gui.libs.image.destroy_action("CollectHelper_labels")
end
if dt.preferences.read('module_CollectHelper','all_and','bool') then
dt.gui.libs.image.destroy_action("CollectHelper_and")
end
end
-- GUI -- -- GUI --
dt.gui.libs.image.register_action( dt.gui.libs.image.register_action(
_("collect: previous"), "CollectHelper_prev", _("collect: previous"),
function() PreviousCollection() end, function() PreviousCollection() end,
_("Sets the Collect parameters to be the previously active parameters") _("Sets the Collect parameters to be the previously active parameters")
) )
if dt.preferences.read('module_CollectHelper','folder','bool') then if dt.preferences.read('module_CollectHelper','folder','bool') then
dt.gui.libs.image.register_action( dt.gui.libs.image.register_action(
_("collect: folder"), "CollectHelper_folder", _("collect: folder"),
function() CollectOnFolder(_ , false) end, function() CollectOnFolder(_ , false) end,
_("Sets the Collect parameters to be the selected images's folder") _("Sets the Collect parameters to be the selected images's folder")
) )
end end
if dt.preferences.read('module_CollectHelper','colors','bool') then if dt.preferences.read('module_CollectHelper','colors','bool') then
dt.gui.libs.image.register_action( dt.gui.libs.image.register_action(
_("collect: color label(s)"), "CollectHelper_labels", _("collect: color label(s)"),
function() CollectOnColors(_ , false) end, function() CollectOnColors(_ , false) end,
_("Sets the Collect parameters to be the selected images's color label(s)") _("Sets the Collect parameters to be the selected images's color label(s)")
) )
end end
if dt.preferences.read('module_CollectHelper','all_and','bool') then if dt.preferences.read('module_CollectHelper','all_and','bool') then
dt.gui.libs.image.register_action( dt.gui.libs.image.register_action(
_("collect: all (AND)"), "CollectHelper_and", _("collect: all (AND)"),
function() CollectOnAll_AND() end, function() CollectOnAll_AND() end,
_("Sets the Collect parameters based on all activated CollectHelper options") _("Sets the Collect parameters based on all activated CollectHelper options")
) )
@@ -204,7 +233,7 @@ end
dt.preferences.register("module_CollectHelper", "all_and", -- name dt.preferences.register("module_CollectHelper", "all_and", -- name
"bool", -- type "bool", -- type
_('CollectHelper: All'), -- label _('CollectHelper: All'), -- label
_('Will create a collect parameter set that utelizes all enabled CollectHelper types (AND)'), -- tooltip _('Will create a collect parameter set that utilizes all enabled CollectHelper types (AND)'), -- tooltip
true -- default true -- default
) )
dt.preferences.register("module_CollectHelper", "colors", -- name dt.preferences.register("module_CollectHelper", "colors", -- name
@@ -219,3 +248,7 @@ dt.preferences.register("module_CollectHelper", "folder", -- name
_('Enable the button that allows you to swap to a collection based on selected image\'s FOLDER location'), -- tooltip _('Enable the button that allows you to swap to a collection based on selected image\'s FOLDER location'), -- tooltip
true -- default true -- default
) )
script_data.destroy = destroy
return script_data

View File

@@ -45,380 +45,435 @@ Select a style, whether you want tags to be copied from the original, and any ad
]] ]]
local dt = require 'darktable' local dt = require 'darktable'
local du = require "lib/dtutils"
local df = require 'lib/dtutils.file' local df = require 'lib/dtutils.file'
local dsys = require 'lib/dtutils.system' local dsys = require 'lib/dtutils.system'
du.check_min_api_version("7.0.0", "HDRmerge")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local mod = 'module_HDRMerge' local mod = 'module_HDRMerge'
local os_path_seperator = '/' local os_path_seperator = '/'
if dt.configuration.running_os == 'windows' then os_path_seperator = '\\' end if dt.configuration.running_os == 'windows' then os_path_seperator = '\\' end
local CURR_API_STRING = dt.configuration.api_version_string
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
local gettext = dt.gettext local gettext = dt.gettext
gettext.bindtextdomain('HDRMerge',dt.configuration.config_dir..'/lua/locale/') gettext.bindtextdomain('HDRMerge', dt.configuration.config_dir..'/lua/locale/')
local function _(msgid) local function _(msgid)
return gettext.dgettext('HDRMerge', msgid) return gettext.dgettext('HDRMerge', msgid)
end end
local temp local temp
local HDRM = { --HDRMerge Program Table local HDRM = { --HDRMerge Program Table
name = 'HDRMerge', name = 'HDRMerge',
bin = '', bin = '',
first_run = true, first_run = true,
install_error = false, install_error = false,
arg_string = '', arg_string = '',
images_string = '', images_string = '',
args = { args = {
bps ={text = '-b ', style = 'integer'}, bps = {text = '-b ', style = 'integer'},
size ={text = '-p ', style = 'string'}, size = {text = '-p ', style = 'string'},
batch ={text = '-B ', style = 'bool'}, batch = {text = '-B ', style = 'bool'},
gap ={text = '-g ', style = 'integer'} gap = {text = '-g ', style = 'integer'}
} }
} }
local GUI = { --GUI Elements Table local GUI = { --GUI Elements Table
HDR = { HDR = {
bps ={}, bps ={},
size ={}, size ={},
batch ={}, batch ={},
gap ={} gap ={}
}, },
Target = { Target = {
style ={}, style ={},
copy_tags ={}, copy_tags ={},
add_tags ={} add_tags ={}
}, },
run = {}, run = {},
stack = {}, stack = {},
options = {}, options = {},
exes = { exes = {
HDRMerge = {}, HDRMerge = {},
update = {}, update = {},
} }
} }
HDRM.module_installed = false
HDRM.event_registered = false
--Detect User Styles-- --Detect User Styles--
local styles = dt.styles local styles = dt.styles
local styles_count = 1 -- 'none' = 1 local styles_count = 1 -- 'none' = 1
for _,i in pairs(dt.styles) do for _,i in pairs(dt.styles) do
if type(i) == 'userdata' then styles_count = styles_count + 1 end if type(i) == 'userdata' then styles_count = styles_count + 1 end
end end
local function InRange(test, low, high) --tests if test value is within range of low and high (inclusive) local function InRange(test, low, high) --tests if test value is within range of low and high (inclusive)
if test >= low and test <= high then if test >= low and test <= high then
return true return true
else else
return false return false
end end
end end
local function GetFileName(full_path) --Parses a full path (path/filename_identifier.extension) into individual parts local function GetFileName(full_path) --Parses a full path (path/filename_identifier.extension) into individual parts
--[[Input: Folder1/Folder2/Folder3/Img_0001.CR2 --[[Input: Folder1/Folder2/Folder3/Img_0001.CR2
Returns: Returns:
path: Folder1/Folder2/Folder3/ path: Folder1/Folder2/Folder3/
filename: Img_0001 filename: Img_0001
identifier: 0001 identifier: 0001
extension: .CR2 extension: .CR2
EX: EX:
path_1, file_1, id_1, ext_1 = GetFileName(full_path_1) path_1, file_1, id_1, ext_1 = GetFileName(full_path_1)
]] ]]
local path = string.match(full_path, '.*[\\/]') local path = string.match(full_path, '.*[\\/]')
local filename = string.gsub(string.match(full_path, '[%w-_]*%.') , '%.' , '' ) local filename = string.gsub(string.match(full_path, '[%w-_]*%.') , '%.' , '' )
local identifier = string.match(filename, '%d*$') local identifier = string.match(filename, '%d*$')
local extension = string.match(full_path, '%.%w*') local extension = string.match(full_path, '%.%w*')
return path, filename, identifier, extension return path, filename, identifier, extension
end end
local function CleanSpaces(text) --removes spaces from the front and back of passed in text local function CleanSpaces(text) --removes spaces from the front and back of passed in text
text = string.gsub(text,'^%s*','') text = string.gsub(text,'^%s*','')
text = string.gsub(text,'%s*$','') text = string.gsub(text,'%s*$','')
return text return text
end end
local function BuildExecuteCmd(prog_table) --creates a program command using elements of the passed in program table local function BuildExecuteCmd(prog_table) --creates a program command using elements of the passed in program table
local result = CleanSpaces(prog_table.bin)..' '..CleanSpaces(prog_table.arg_string)..' '..CleanSpaces(prog_table.images_string) local result = CleanSpaces(prog_table.bin)..' '..CleanSpaces(prog_table.arg_string)..' '..CleanSpaces(prog_table.images_string)
return result return result
end end
local function PreCall(prog_tbl) --looks to see if this is the first call, if so checks to see if program is installed properly local function PreCall(prog_tbl) --looks to see if this is the first call, if so checks to see if program is installed properly
for _,prog in pairs(prog_tbl) do for _,prog in pairs(prog_tbl) do
if prog.first_run then if prog.first_run then
prog.bin = df.check_if_bin_exists(prog.name) prog.bin = df.check_if_bin_exists(prog.name)
if not prog.bin then if not prog.bin then
prog.install_error = true prog.install_error = true
dt.preferences.write(mod, 'bin_exists', 'bool', false) dt.preferences.write(mod, 'bin_exists', 'bool', false)
else else
prog.bin = CleanSpaces(prog.bin) prog.bin = CleanSpaces(prog.bin)
end end
prog.first_run = false prog.first_run = false
end
end
if not dt.preferences.read(mod, 'bin_exists', 'bool') then
GUI.stack.active = 2
dt.print(_('please update you binary locatoin'))
end end
end
if not dt.preferences.read(mod, 'bin_exists', 'bool') then
GUI.stack.active = 2
dt.print(_('please update you binary location'))
end
end end
local function ExeUpdate(prog_tbl) local function ExeUpdate(prog_tbl)
dt.preferences.write(mod, 'bin_exists', 'bool', true) dt.preferences.write(mod, 'bin_exists', 'bool', true)
for _,prog in pairs(prog_tbl) do for _,prog in pairs(prog_tbl) do
dt.preferences.write('executable_paths', prog.name, 'string', GUI.exes[prog.name].value) dt.preferences.write('executable_paths', prog.name, 'string', GUI.exes[prog.name].value)
prog.bin = df.check_if_bin_exists(prog.name) prog.bin = df.check_if_bin_exists(prog.name)
if not prog.bin then if not prog.bin then
prog.install_error = true prog.install_error = true
dt.preferences.write(mod, 'bin_exists', 'bool', false) dt.preferences.write(mod, 'bin_exists', 'bool', false)
else
prog.bin = CleanSpaces(prog.bin)
end
prog.first_run = false
end
if dt.preferences.read(mod, 'bin_exists', 'bool') then
GUI.stack.active = 1
dt.print(_('update successful'))
else else
dt.print(_('update unsuccessful, please try again')) prog.bin = CleanSpaces(prog.bin)
end end
prog.first_run = false
end
if dt.preferences.read(mod, 'bin_exists', 'bool') then
GUI.stack.active = 1
dt.print(_('update successful'))
else
dt.print(_('update unsuccessful, please try again'))
end
end end
local function UpdateActivePreference() --sliders & entry boxes do not have a click/changed callback, so their values must be saved to the active preference local function UpdateActivePreference() --sliders & entry boxes do not have a click/changed callback, so their values must be saved to the active preference
temp = GUI.HDR.gap.value temp = GUI.HDR.gap.value
dt.preferences.write(mod, 'active_gap', 'integer', temp) dt.preferences.write(mod, 'active_gap', 'integer', temp)
temp = GUI.Target.add_tags.text temp = GUI.Target.add_tags.text
dt.preferences.write(mod, 'active_add_tags', 'string', temp) dt.preferences.write(mod, 'active_add_tags', 'string', temp)
end end
local function main() local function main()
PreCall({HDRM}) --check if furst run then check if install OK PreCall({HDRM}) --check if furst run then check if install OK
if HDRM.install_error then if HDRM.install_error then
dt.print_error(_('HDRMerge install issue')) dt.print_error(_('HDRMerge install issue'))
dt.print(_('HDRMerge install issue, please ensure the binary path is proper')) dt.print(_('HDRMerge install issue, please ensure the binary path is proper'))
return return
end
images = dt.gui.selection() --get selected images
if #images < 2 then --ensure enough images selected
dt.print(_('not enough images selected, select at least 2 images to merge'))
return
end
UpdateActivePreference() --save current gui elements to active preference so those values will be pre-loaded at next startup
--create image string and output path
HDRM.images_string = ''
local out_path = ''
local smallest_id = math.huge
local smallest_name = ''
local largest_id = 0
local source_raw = {}
for _,image in pairs(images) do --loop to concat the images string, also track the image indexes for use in creating the final image name (eg; IMG_1034-1037.dng)
local curr_image = image.path..os_path_seperator..image.filename
HDRM.images_string = HDRM.images_string..df.sanitize_filename(curr_image)..' '
out_path = image.path
_unused, source_name, source_id = GetFileName(image.filename)
source_id = tonumber(source_id) or 0
if source_id < smallest_id then
smallest_id = source_id
smallest_name = source_name
source_raw = image
end end
images = dt.gui.selection() --get selected images if source_id > largest_id then largest_id = source_id end
if #images < 2 then --ensure enough images selected end
dt.print(_('not enough images selected, select at least 2 images to merge')) out_path = out_path..os_path_seperator..smallest_name..'-'..largest_id..'.dng'
return out_path = df.create_unique_filename(out_path)
--create argument string
HDRM.arg_string = HDRM.args.bps.text..GUI.HDR.bps.value..' '..HDRM.args.size.text..GUI.HDR.size.value..' '
if GUI.HDR.batch.value then
HDRM.arg_string = HDRM.arg_string..HDRM.args.batch.text..HDRM.args.gap.text..math.floor(GUI.HDR.gap.value)..' -a'
else
HDRM.arg_string = HDRM.arg_string..'-o '..df.sanitize_filename(out_path)
end
-- create run command and execute
local run_cmd = BuildExecuteCmd(HDRM)
resp = dsys.external_command(run_cmd)
if resp == 0 and not GUI.HDR.batch.value then
local imported = dt.database.import(out_path) -- import the new file
if GUI.Target.style.selected > 1 then -- apply selected style
local set_style = styles[GUI.Target.style.selected - 1]
dt.styles.apply(set_style , imported)
end end
if GUI.Target.copy_tags.value then -- copy tags from the original file (ignore 'darktable' generated tags)
UpdateActivePreference() --save current gui elements to active preference so those values will be pre-loaded at next startup local all_tags = dt.tags.get_tags(source_raw)
for _,tag in pairs(all_tags) do
--create image string and output path if string.match(tag.name, 'darktable|') == nil then dt.tags.attach(tag, imported) end
HDRM.images_string = '' end
local out_path = ''
local smallest_id = math.huge
local smallest_name = ''
local largest_id = 0
local source_raw = {}
for _,image in pairs(images) do --loop to concat the images string, also track the image indexes for use in creating the final image name (eg; IMG_1034-1037.dng)
local curr_image = image.path..os_path_seperator..image.filename
HDRM.images_string = HDRM.images_string..df.sanitize_filename(curr_image)..' '
out_path = image.path
_, source_name, source_id = GetFileName(image.filename)
source_id = tonumber(source_id)
if source_id < smallest_id then
smallest_id = source_id
smallest_name = source_name
source_raw = image
end
if source_id > largest_id then largest_id = source_id end
end end
out_path = out_path..os_path_seperator..smallest_name..'-'..largest_id..'.dng' local set_tag = GUI.Target.add_tags.text
out_path = df.create_unique_filename(out_path) if set_tag ~= nil then -- add additional user-specified tags
for tag in string.gmatch(set_tag, '[^,]+') do
--create argument string tag = CleanSpaces(tag)
HDRM.arg_string = HDRM.args.bps.text..GUI.HDR.bps.value..' '..HDRM.args.size.text..GUI.HDR.size.value..' ' tag = dt.tags.create(tag)
if GUI.HDR.batch.value then dt.tags.attach(tag, imported)
HDRM.arg_string = HDRM.arg_string..HDRM.args.batch.text..HDRM.args.gap.text..math.floor(GUI.HDR.gap.value)..' -a' end
else
HDRM.arg_string = HDRM.arg_string..'-o '..df.sanitize_filename(out_path)
end
-- create run command and execute
local run_cmd = BuildExecuteCmd(HDRM)
resp = dsys.external_command(run_cmd)
if resp == 0 and not GUI.HDR.batch.value then
local imported = dt.database.import(out_path) -- import the new file
if GUI.Target.style.selected > 1 then -- apply selected style
local set_style = styles[GUI.Target.style.selected - 1]
dt.styles.apply(set_style , imported)
end
if GUI.Target.copy_tags.value then -- copy tags from the original file (ignore 'darktable' generated tags)
local all_tags = dt.tags.get_tags(source_raw)
for _,tag in pairs(all_tags) do
if string.match(tag.name, 'darktable|') == nil then dt.tags.attach(tag, imported) end
end
end
local set_tag = GUI.Target.add_tags.text
if set_tag ~= nil then -- add additional user-specified tags
for tag in string.gmatch(set_tag, '[^,]+') do
tag = CleanSpaces(tag)
tag = dt.tags.create(tag)
dt.tags.attach(tag, imported)
end
end
dt.print(_('HDRMerge completed successfully'))
else
dt.print_error(_('HDRMerge failed'))
dt.print(_('HDRMerge failed'))
end end
dt.print(_('HDRMerge completed successfully'))
else
dt.print_error(_('HDRMerge failed'))
dt.print(_('HDRMerge failed'))
end
end end
local function install_module()
if not HDRM.module_installed then
dt.register_lib( -- register HDRMerge module
'HDRMerge_Lib', -- Module name
_('HDRMerge'), -- name
true, -- expandable
true, -- resetable
{[dt.gui.views.lighttable] = {'DT_UI_CONTAINER_PANEL_RIGHT_CENTER', 99}}, -- containers
dt.new_widget('box'){
orientation = 'vertical',
GUI.stack
}
)
HDRM.module_installed = true
end
end
local function destroy()
dt.gui.libs["HDRMerge_Lib"].visible = false
end
local function restart()
dt.gui.libs["HDRMerge_Lib"].visible = true
end
-- GUI Elements -- -- GUI Elements --
local lbl_hdr = dt.new_widget('section_label'){ local lbl_hdr = dt.new_widget('section_label'){
label = _('HDRMerge options') label = _('HDRMerge options')
} }
temp = dt.preferences.read(mod, 'active_bps_ind', 'integer') temp = dt.preferences.read(mod, 'active_bps_ind', 'integer')
if not InRange(temp, 1, 3) then temp = 3 end if not InRange(temp, 1, 3) then temp = 3 end
GUI.HDR.bps = dt.new_widget('combobox'){ GUI.HDR.bps = dt.new_widget('combobox'){
label = _('bits per sample'), label = _('bits per sample'),
tooltip =_('number of bits per sample in the output image'), tooltip =_('number of bits per sample in the output image'),
selected = temp, selected = temp,
'16','24','32', '16','24','32',
changed_callback = function(self) changed_callback = function(self)
dt.preferences.write(mod, 'active_bps', 'integer', self.value) dt.preferences.write(mod, 'active_bps', 'integer', self.value)
dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected) dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected)
end, end,
reset_callback = function(self) reset_callback = function(self)
self.selected = 3 self.selected = 3
dt.preferences.write(mod, 'active_bps', 'integer', self.value) dt.preferences.write(mod, 'active_bps', 'integer', self.value)
dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected) dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected)
end end
} }
temp = dt.preferences.read(mod, 'active_size_ind', 'integer') temp = dt.preferences.read(mod, 'active_size_ind', 'integer')
if not InRange(temp, 1, 3) then temp = 2 end if not InRange(temp, 1, 3) then temp = 2 end
GUI.HDR.size = dt.new_widget('combobox'){ GUI.HDR.size = dt.new_widget('combobox'){
label = _('embedded preview size'), label = _('embedded preview size'),
tooltip =_('size of the embedded preview in output image'), tooltip =_('size of the embedded preview in output image'),
selected = temp, selected = temp,
_('none'),_('half'),_('full'), _('none'),_('half'),_('full'),
changed_callback = function(self) changed_callback = function(self)
dt.preferences.write(mod, 'active_size', 'string', self.value) dt.preferences.write(mod, 'active_size', 'string', self.value)
dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected) dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected)
end, end,
reset_callback = function(self) reset_callback = function(self)
self.selected = 2 self.selected = 2
dt.preferences.write(mod, 'active_size', 'string', self.value) dt.preferences.write(mod, 'active_size', 'string', self.value)
dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected) dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected)
end end
} }
GUI.HDR.batch = dt.new_widget('check_button'){ GUI.HDR.batch = dt.new_widget('check_button'){
label = _('batch mode'), label = _('batch mode'),
value = dt.preferences.read(mod, 'active_batch', 'bool'), value = dt.preferences.read(mod, 'active_batch', 'bool'),
tooltip = _('enable batch mode operation \nNOTE: resultant files will NOT be auto-imported'), tooltip = _('enable batch mode operation \nNOTE: resultant files will NOT be auto-imported'),
clicked_callback = function(self) clicked_callback = function(self)
dt.preferences.write(mod, 'active_batch', 'bool', self.value) dt.preferences.write(mod, 'active_batch', 'bool', self.value)
GUI.HDR.gap.sensitive = self.value GUI.HDR.gap.sensitive = self.value
end, end,
reset_callback = function(self) self.value = false end reset_callback = function(self) self.value = false end
} }
temp = dt.preferences.read(mod, 'active_gap', 'integer') temp = dt.preferences.read(mod, 'active_gap', 'integer')
if not InRange(temp, 1, 3600) then temp = 3 end if not InRange(temp, 1, 3600) then temp = 3 end
GUI.HDR.gap = dt.new_widget('slider'){ GUI.HDR.gap = dt.new_widget('slider'){
label = _('batch gap [sec.]'), label = _('batch gap [sec.]'),
tooltip = _('gap, in seconds, between batch mode groups'), tooltip = _('gap, in seconds, between batch mode groups'),
soft_min = 1, soft_min = 1,
soft_max = 30, soft_max = 30,
hard_min = 1, hard_min = 1,
hard_max = 3600, hard_max = 3600,
step = 1, step = 1,
digits = 0, digits = 0,
value = temp, value = temp,
sensitive = GUI.HDR.batch.value, sensitive = GUI.HDR.batch.value,
reset_callback = function(self) reset_callback = function(self)
self.value = 3 self.value = 3
end end
} }
local lbl_import = dt.new_widget('section_label'){ local lbl_import = dt.new_widget('section_label'){
label = _('import options') label = _('import options')
} }
GUI.Target.style = dt.new_widget('combobox'){ GUI.Target.style = dt.new_widget('combobox'){
label = _('apply style on import'), label = _('apply style on import'),
tooltip = _('Apply selected style on auto-import to newly created image'), tooltip = _('Apply selected style on auto-import to newly created image'),
selected = 1, selected = 1,
_('none'), _('none'),
changed_callback = function(self) changed_callback = function(self)
dt.preferences.write(mod, 'active_style', 'string', self.value) dt.preferences.write(mod, 'active_style', 'string', self.value)
dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected) dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected)
end, end,
reset_callback = function(self) reset_callback = function(self)
self.selected = 1 self.selected = 1
dt.preferences.write(mod, 'active_style', 'string', self.value) dt.preferences.write(mod, 'active_style', 'string', self.value)
dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected) dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected)
end end
} }
for k=1, (styles_count-1) do for k=1, (styles_count-1) do
GUI.Target.style[k+1] = styles[k].name GUI.Target.style[k+1] = styles[k].name
end end
temp = dt.preferences.read(mod, 'active_style_ind', 'integer') temp = dt.preferences.read(mod, 'active_style_ind', 'integer')
if not InRange(temp, 1, styles_count) then temp = 1 end if not InRange(temp, 1, styles_count) then temp = 1 end
GUI.Target.style.selected = temp GUI.Target.style.selected = temp
GUI.Target.copy_tags = dt.new_widget('check_button'){ GUI.Target.copy_tags = dt.new_widget('check_button'){
label = _('copy tags'), label = _('copy tags'),
value = dt.preferences.read(mod, 'active_copy_tags', 'bool'), value = dt.preferences.read(mod, 'active_copy_tags', 'bool'),
tooltip = _('copy tags from first source image'), tooltip = _('copy tags from first source image'),
clicked_callback = function(self) dt.preferences.write(mod, 'active_copy_tags', 'bool', self.value) end, clicked_callback = function(self) dt.preferences.write(mod, 'active_copy_tags', 'bool', self.value) end,
reset_callback = function(self) self.value = true end reset_callback = function(self) self.value = true end
} }
temp = dt.preferences.read(mod, 'active_add_tags', 'string') temp = dt.preferences.read(mod, 'active_add_tags', 'string')
if temp == '' then temp = nil end if temp == '' then temp = nil end
GUI.Target.add_tags = dt.new_widget('entry'){ GUI.Target.add_tags = dt.new_widget('entry'){
tooltip = _('Additional tags to be added on import. Seperate with commas, all spaces will be removed'), tooltip = _('Additional tags to be added on import. Seperate with commas, all spaces will be removed'),
text = temp, text = temp,
placeholder = _('Enter tags, seperated by commas'), placeholder = _('Enter tags, seperated by commas'),
editable = true editable = true
} }
GUI.run = dt.new_widget('button'){ GUI.run = dt.new_widget('button'){
label = _('merge'), label = _('merge'),
tooltip =_('run HDRMerge with the above specified settings'), tooltip =_('run HDRMerge with the above specified settings'),
clicked_callback = function() main() end clicked_callback = function() main() end
} }
GUI.exes.HDRMerge = dt.new_widget('file_chooser_button'){ GUI.exes.HDRMerge = dt.new_widget('file_chooser_button'){
title = _('Select HDRmerge executable'), title = _('Select HDRmerge executable'),
value = df.get_executable_path_preference(HDRM.name), value = df.get_executable_path_preference(HDRM.name),
is_directory = false is_directory = false
} }
GUI.exes.update = dt.new_widget('button'){ GUI.exes.update = dt.new_widget('button'){
label = _('update'), label = _('update'),
tooltip =_('update the binary path with current value'), tooltip =_('update the binary path with current value'),
clicked_callback = function() ExeUpdate({HDRM}) end clicked_callback = function() ExeUpdate({HDRM}) end
} }
GUI.options = dt.new_widget('box'){ GUI.options = dt.new_widget('box'){
orientation = 'vertical', orientation = 'vertical',
lbl_hdr, lbl_hdr,
GUI.HDR.bps, GUI.HDR.bps,
GUI.HDR.size, GUI.HDR.size,
GUI.HDR.batch, GUI.HDR.batch,
GUI.HDR.gap, GUI.HDR.gap,
lbl_import, lbl_import,
GUI.Target.style, GUI.Target.style,
GUI.Target.copy_tags, GUI.Target.copy_tags,
GUI.Target.add_tags, GUI.Target.add_tags,
GUI.run GUI.run
} }
local exes_box = dt.new_widget('box'){ local exes_box = dt.new_widget('box'){
orientation = 'vertical', orientation = 'vertical',
GUI.exes.HDRMerge, GUI.exes.HDRMerge,
GUI.exes.update GUI.exes.update
} }
GUI.stack = dt.new_widget('stack'){ GUI.stack = dt.new_widget('stack'){
GUI.options, GUI.options,
exes_box exes_box
} }
if dt.preferences.read(mod, 'bin_exists', 'bool') then if dt.preferences.read(mod, 'bin_exists', 'bool') then
GUI.stack.active = 1 GUI.stack.active = 1
else else
GUI.stack.active = 2 GUI.stack.active = 2
end end
dt.register_lib( -- register HDRMerge module if dt.gui.current_view().id == "lighttable" then
'HDRMerge_Lib', -- Module name install_module()
_('HDRMerge'), -- name else
true, -- expandable if not HDRM.event_registered then
true, -- resetable dt.register_event(
{[dt.gui.views.lighttable] = {'DT_UI_CONTAINER_PANEL_RIGHT_CENTER', 99}}, -- containers "HDRmerge", "view-changed",
dt.new_widget('box'){ function(event, old_view, new_view)
orientation = 'vertical', if new_view.name == "lighttable" and old_view.name == "darkroom" then
GUI.stack install_module()
} end
) end
)
HDRM.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data

View File

@@ -50,11 +50,24 @@
local darktable = require("darktable") local darktable = require("darktable")
local du = require "lib/dtutils" local du = require "lib/dtutils"
du.check_min_api_version("3.0.0", "LabelsToTags") du.check_min_api_version("7.0.0", "LabelsToTags")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Lua 5.3 no longer has "unpack" but "table.unpack" -- Lua 5.3 no longer has "unpack" but "table.unpack"
unpack = unpack or table.unpack unpack = unpack or table.unpack
local ltt = {}
ltt.module_installed = false
ltt.event_registered = false
local LIB_ID = "LabelsToTags" local LIB_ID = "LabelsToTags"
-- Helper functions: BEGIN -- Helper functions: BEGIN
@@ -139,16 +152,16 @@ local mappingComboBox = darktable.new_widget("combobox"){
tooltip = getComboboxTooltip(), tooltip = getComboboxTooltip(),
reset_callback = function(selfC) reset_callback = function(selfC)
if selfC == nil then if selfC == nil then
return return
end end
i = 1 i = 1
for _,m in pairs(keySet(getAvailableMappings())) do for _,m in pairs(keySet(getAvailableMappings())) do
selfC[i] = m selfC[i] = m
i = i+1 i = i+1
end end
n = #selfC n = #selfC
for j = i,n do for j = i,n do
selfC[i] = nil selfC[i] = nil
end end
selfC.value = 1 selfC.value = 1
selfC.tooltip = getComboboxTooltip() selfC.tooltip = getComboboxTooltip()
@@ -167,24 +180,24 @@ local function doTagging(selfC)
local tagsToApply = {} local tagsToApply = {}
local hash = generateLabelHash(img) local hash = generateLabelHash(img)
for k,v in pairs(availableMappings[mappingComboBox.value]) do for k,v in pairs(availableMappings[mappingComboBox.value]) do
if hashMatch(hash,k) then if hashMatch(hash,k) then
for _,tag in ipairs(v) do for _,tag in ipairs(v) do
tagsToApply[tag] = true tagsToApply[tag] = true
end end
end end
end end
for k,_ in pairs(tagsToApply) do for k,_ in pairs(tagsToApply) do
if memoizedTags[k] == nil then if memoizedTags[k] == nil then
memoizedTags[k] = darktable.tags.create(k) memoizedTags[k] = darktable.tags.create(k)
end end
darktable.tags.attach(memoizedTags[k],img) darktable.tags.attach(memoizedTags[k],img)
end end
job.percent = job.percent + pctIncrement job.percent = job.percent + pctIncrement
end end
job.valid = false job.valid = false
end end
local my_widget = darktable.new_widget("box") { ltt.my_widget = darktable.new_widget("box") {
orientation = "vertical", orientation = "vertical",
mappingComboBox, mappingComboBox,
darktable.new_widget("button") { darktable.new_widget("button") {
@@ -203,20 +216,37 @@ darktable.register_tag_mapping = function(name, mapping)
end end
for pattern,tags in pairs(mapping) do for pattern,tags in pairs(mapping) do
if string.match(pattern,PATTERN_PATTERN) == nil then if string.match(pattern,PATTERN_PATTERN) == nil then
darktable.print_error("In tag mapping '" .. name .. "': Pattern '" .. pattern .. "' invalid") darktable.print_error("In tag mapping '" .. name .. "': Pattern '" .. pattern .. "' invalid")
return return
end end
for _,tag in ipairs(tags) do for _,tag in ipairs(tags) do
if type(tag) ~= "string" then if type(tag) ~= "string" then
darktable.print_error("In tag mapping '" .. name .. "': All tag mappings must be lists of strings") darktable.print_error("In tag mapping '" .. name .. "': All tag mappings must be lists of strings")
return return
end end
end end
end end
availableMappings[name] = mapping availableMappings[name] = mapping
mappingComboBox.reset_callback(mappingComboBox) mappingComboBox.reset_callback(mappingComboBox)
end end
local function install_module()
if not ltt.module_installed then
darktable.register_lib(LIB_ID,"labels to tags",true,true,{
[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",20},
},ltt.my_widget,nil,nil)
ltt.module_installed = true
end
end
local function destroy()
darktable.gui.libs[LIB_ID].visible = false
end
local function restart()
darktable.gui.libs[LIB_ID].visible = true
end
--[[ --[[
darktable.register_tag_mapping("Example", darktable.register_tag_mapping("Example",
{ ["+----*"] = { "Red", "Only red" }, { ["+----*"] = { "Red", "Only red" },
@@ -229,6 +259,25 @@ darktable.register_tag_mapping("Example",
["*****R"] = { "Rejected" } }) ["*****R"] = { "Rejected" } })
]] ]]
darktable.register_lib(LIB_ID,"labels to tags",true,true,{ if darktable.gui.current_view().id == "lighttable" then
[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",20}, install_module()
},my_widget,nil,nil) else
if not ltt.event_registered then
darktable.register_event(
LIB_ID, "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
ltt.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data

View File

@@ -2,6 +2,8 @@
OpenInExplorer plugin for darktable OpenInExplorer plugin for darktable
copyright (c) 2018 Kevin Ertel copyright (c) 2018 Kevin Ertel
Update 2020 and macOS support by Volker Lenhardt
Linux support 2020 by Bill Ferguson
darktable is free software: you can redistribute it and/or modify darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,18 +20,29 @@ OpenInExplorer plugin for darktable
]] ]]
--[[About this plugin --[[About this plugin
This plugin adds the module "OpenInExplorer" to darktable's lighttable view This plugin adds the module "OpenInExplorer" to darktable's lighttable view.
----REQUIRED SOFTWARE---- ----REQUIRED SOFTWARE----
Microsoft Windows or Linux with installed Nautilus Apple macOS, Microsoft Windows or Linux
----USAGE---- ----USAGE----
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts ) Install: (see here for more detail: https://github.com/darktable-org/lua-scripts )
1) Copy this file in to your "lua/contrib" folder where all other scripts reside. 1) Copy this file into your "lua/contrib" folder where all other scripts reside.
2) Require this file in your luarc file, as with any other dt plug-in 2) Require this file in your luarc file, as with any other dt plug-in
Select the photo(s) you wish to find in explorer and press "Go to Folder". Select the photo(s) you wish to find in your operating system's file manager and press "show in file explorer" in the "selected images" section.
A file explorer window will be opened for each selected file at the file's location; the file will be highlighted.
- Nautilus (Linux), Explorer (Windows), and Finder (macOS prior to Mojave) will open one window for each selected image at the file's location. The file name will be highlighted.
- On macOS Mojave and Catalina the Finder will open one window for each different directory. In these windows only the last one of the corresponding files will be highlighted (bug or feature?).
- Dolphin (Linux) will open one window with tabs for the different directories. All the selected images' file names are highlighted in their respective directories.
As an alternative option you can choose to show the image file names as symbolic links in an arbitrary directory. Go to preferences|Lua options. This option is not available for Windows users as on Windows solely admins are allowed to create links.
- Pros: You do not clutter up your display with multiple windows. So there is no need to limit the number of selections.
- Cons: If you want to work with the files you are one step behind the original data.
----KNOWN ISSUES---- ----KNOWN ISSUES----
]] ]]
@@ -38,76 +51,188 @@ local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
local df = require "lib/dtutils.file" local df = require "lib/dtutils.file"
local dsys = require "lib/dtutils.system" local dsys = require "lib/dtutils.system"
local gettext = dt.gettext
--Check API version --Check API version
du.check_min_api_version("5.0.0", "OpenInExplorer") du.check_min_api_version("7.0.0", "OpenInExplorer")
local PS = dt.configuration.running_os == "windows" and "\\" or "/" -- return data structure for script_manager
--Detect OS and modify accordingly-- local script_data = {}
local proper_install = false
if dt.configuration.running_os ~= "macos" then script_data.destroy = nil -- function to destory the script
proper_install = true script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
else script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
dt.print_error('OpenInExplorer plug-in only supports Windows and Linux at this time') script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("OpenInExplorer",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("OpenInExplorer", msgid)
end
local act_os = dt.configuration.running_os
local PS = act_os == "windows" and "\\" or "/"
--Detect OS and quit if it is not supported.
if act_os ~= "macos" and act_os ~= "windows" and act_os ~= "linux" then
dt.print(_("OpenInExplorer plug-in only supports Linux, macOS, and Windows at this time"))
dt.print_error("OpenInExplorer plug-in only supports Linux, macOS, and Windows at this time")
return return
end end
local use_links, links_dir = false, ""
if act_os ~= "windows" then
use_links = dt.preferences.read("OpenInExplorer", "use_links", "bool")
links_dir = dt.preferences.read("OpenInExplorer", "linked_image_files_dir", "string")
end
-- FUNCTIONS -- --Check if the directory exists that was chosen for the file links. Return boolean.
local function check_if_links_dir_exists()
local dir_exists = true
if not links_dir then
--Just for paranoic reasons. I tried, but I couldn't devise a setting for a nil value.
dt.print(_("No links directory selected.\nPlease check the dt preferences (lua options)"))
dt.print_error("OpenInExplorer: No links directory selected")
dir_exists = false
elseif not df.check_if_file_exists(links_dir) then
dt.print(string.format(_("Links directory '%s' not found.\nPlease check the dt preferences (lua options)"), links_dir))
dt.print_error(string.format("OpenInExplorer: Links directory '%s' not found", links_dir))
dir_exists = false
end
return dir_exists
end
local function open_in_explorer() --Open in Explorer --Format strings for the commands to open the corresponding OS's file manager.
local images = dt.gui.selection() local open_dir = {}
local curr_image = "" open_dir.windows = "explorer.exe /n, %s"
if #images == 0 then open_dir.macos = "open %s"
dt.print('please select an image') open_dir.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowFolders ass 1 %s ""]]
elseif #images <= 15 then
for _,image in pairs(images) do local open_files = {}
curr_image = image.path..PS..image.filename open_files.windows = "explorer.exe /select, %s"
local run_cmd = "explorer.exe /select, "..curr_image open_files.macos = "open -Rn %s"
dt.print_log("OpenInExplorer run_cmd = "..run_cmd) open_files.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowItems ass %d %s ""]]
resp = dsys.external_command(run_cmd)
--Call the file mangager for each selected image on Linux.
--There is one call to busctl containing a list of all the image file names.
local function call_list_of_files(selected_images)
local current_image, file_uris, run_cmd = "", "", ""
for _, image in pairs(selected_images) do
current_image = image.path..PS..image.filename
file_uris = file_uris .. df.sanitize_filename("file://" .. current_image) .. " "
dt.print_log("file_uris is " .. file_uris)
end
run_cmd = string.format(open_files.linux, #selected_images, file_uris)
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
dsys.external_command(run_cmd)
end
--Call the file manager for each selected image on Windows and macOS.
local function call_file_by_file(selected_images)
local current_image, run_cmd = "", ""
for _, image in pairs(selected_images) do
current_image = image.path..PS..image.filename
run_cmd = string.format(open_files[act_os], df.sanitize_filename(current_image))
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
dsys.external_command(run_cmd)
end
end
--Create a link for each selected image, and finally call the file manager.
local function set_links(selected_images)
local current_image, link_target, run_cmd, k = "", "", "", nil
for k, image in pairs(selected_images) do
current_image = image.path..PS..image.filename
link_target = df.create_unique_filename(links_dir .. PS .. image.filename)
run_cmd = string.format("ln -s %s %s", df.sanitize_filename(current_image), df.sanitize_filename(link_target))
--[[
In case Windows will allow normal users to create soft links:
if act_os == "windows" then
run_cmd = string.format("mklink %s %s", df.sanitize_filename(link_target), df.sanitize_filename(current_image))
end end
else ]]
dt.print('please select fewer images (max 15)') if dsys.external_command(run_cmd) ~= 0 then
end dt.print(_("Failed to create links. Missing rights?"))
end dt.print_error("OpenInExplorer: Failed to create links")
return
local function open_in_nautilus() --Open in Nautilus
local images = dt.gui.selection()
local curr_image = ""
if #images == 0 then
dt.print('please select an image')
elseif #images <= 15 then
for _,image in pairs(images) do
curr_image = image.path..PS..image.filename
local run_cmd = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowItems ass 1 ]] .. df.sanitize_filename("file://"..curr_image) .. [[ ""]]
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
resp = dsys.external_command(run_cmd)
end end
end
--The URI format is necessary only for the Linux busctl command.
--But it is accepted by the Windows Explorer and macOS's Finder all the same.
run_cmd = string.format(open_dir[act_os], df.sanitize_filename("file://"..links_dir))
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
dsys.external_command(run_cmd)
end
--The working function that starts the particular task.
local function open_in_fmanager()
local images = dt.gui.selection()
if #images == 0 then
dt.print(_("Please select an image"))
else else
dt.print('please select fewer images (max 15)') if use_links and not check_if_links_dir_exists() then
return
end
if #images > 15 and not use_links then
dt.print(_("Please select fewer images (max. 15)"))
elseif use_links then
set_links(images)
else
if act_os == "linux" then
call_list_of_files(images)
else
call_file_by_file(images)
end
end
end end
end end
local function open_in_filemanager() --Open local function destroy()
--Inits-- dt.gui.libs.image.destroy_action("OpenInExplorer")
if not proper_install then dt.destroy_event("OpenInExplorer", "shortcut")
return if act_os ~= "windows" then
dt.preferences.destroy("OpenInExplorer", "linked_image_files_dir")
end end
dt.preferences.destroy("OpenInExplorer", "use_links")
if (dt.configuration.running_os == "windows") then
open_in_explorer()
elseif (dt.configuration.running_os == "linux") then
open_in_nautilus()
end
end end
-- GUI -- -- GUI --
if proper_install then dt.gui.libs.image.register_action(
dt.gui.libs.image.register_action( "OpenInExplorer", _("show in file explorer"),
"show in file explorer", function() open_in_fmanager() end,
function() open_in_filemanager() end, _("Open the file manager at the selected image's location")
"Opens File Explorer at the selected image's location" )
if act_os ~= "windows" then
dt.preferences.register("OpenInExplorer", "linked_image_files_dir", -- name
"directory", -- type
_("OpenInExplorer: linked files directory"), -- label
_("Directory to store the links to the file names. Requires restart to take effect"), -- tooltip
"Links to image files", -- default
dt.new_widget("file_chooser_button"){
title = _("Select directory"),
is_directory = true,
}
)
dt.preferences.register("OpenInExplorer", "use_links", -- name
"bool", -- type
_("OpenInExplorer: use links"), -- label
_("Use links instead of multiple windows. Requires restart to take effect"), -- tooltip
false, -- default
""
) )
end end
dt.register_event(
"OpenInExplorer", "shortcut",
function(event, shortcut) open_in_fmanager() end,
"OpenInExplorer"
)
script_data.destroy = destroy
return script_data

251
contrib/RL_out_sharp.lua Normal file
View File

@@ -0,0 +1,251 @@
--[[
Richardson-Lucy output sharpening for darktable using GMic
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/>.
]]
--[[
DESCRIPTION
RL_out_sharp.lua - Richardson-Lucy output sharpening using GMic
This script provides a new target storage "RL output sharpen".
Images exported will be sharpened using GMic (RL deblur algorithm)
REQUIRED SOFTWARE
GMic command line interface (CLI) https://gmic.eu/download.shtml
USAGE
* require this script from main lua file
* in lua preferences, select the GMic cli executable
* from "export selected", choose "RL output sharpen"
* configure output folder
* configure RL parameters with sliders
* configure temp files format and quality, jpg 8bpp (good quality)
and tif 16bpp (best quality) are supported
* configure other export options (size, etc.)
* export, images will be first exported in the temp format, then sharpened
* sharpened images will be stored in jpg format in the output folder
EXAMPLE
set sigma = 0.7, iterations = 10, jpeg output quality = 95,
to correct blur due to image resize for web usage
CAVEATS
MAC compatibility not tested
Although Darktable can handle file names containing spaces, GMic cli cannot,
so if you want to use this script please make sure that your images do not
have spaces in the file name and path
BUGS, COMMENTS, SUGGESTIONS
send to Marco Carrarini, marco.carrarini@gmail.com
CHANGES
* 20200308 - initial version
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local dtsys = require "lib/dtutils.system"
-- module name
local MODULE_NAME = "RL_out_sharp"
-- check API version
du.check_min_api_version("7.0.0", MODULE_NAME)
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- OS compatibility
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
-- translation
local gettext = dt.gettext
gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
local function _(msgid)
return gettext.dgettext(MODULE_NAME, msgid)
end
-- initialize module preferences
if not dt.preferences.read(MODULE_NAME, "initialized", "bool") then
dt.preferences.write(MODULE_NAME, "sigma", "string", "0.7")
dt.preferences.write(MODULE_NAME, "iterations", "string", "10")
dt.preferences.write(MODULE_NAME, "jpg_quality", "string", "95")
dt.preferences.write(MODULE_NAME, "initialized", "bool", true)
end
-- setup export ---------------------------------------------------------------
local function setup_export(storage, img_format, image_table, high_quality, extra_data)
-- set 16bpp if format is tif
if img_format.extension == "tif" then
img_format.bpp = 16
end
end
-- temp export formats: jpg and tif are supported -----------------------------
local function supported(storage, img_format)
return (img_format.extension == "jpg") or (img_format.extension == "tif")
end
-- export and sharpen images --------------------------------------------------
local function export2RL(storage, image_table, extra_data)
local temp_name, new_name, run_cmd, result
local input_file, output_file, options
-- read parameters
local gmic = dt.preferences.read(MODULE_NAME, "gmic_exe", "string")
if gmic == "" then
dt.print(_("GMic executable not configured"))
return
end
gmic = df.sanitize_filename(gmic)
local output_folder = output_folder_selector.value
local sigma_str = string.gsub(string.format("%.2f", sigma_slider.value), ",", ".")
local iterations_str = string.format("%.0f", iterations_slider.value)
local jpg_quality_str = string.format("%.0f", jpg_quality_slider.value)
-- save preferences
dt.preferences.write(MODULE_NAME, "sigma", "string", sigma_str)
dt.preferences.write(MODULE_NAME, "iterations", "string", iterations_str)
dt.preferences.write(MODULE_NAME, "jpg_quality", "string", jpg_quality_str)
local gmic_operation = " -deblur_richardsonlucy "..sigma_str..","..iterations_str..",1"
local i = 0
for image, temp_name in pairs(image_table) do
i = i + 1
dt.print(_("sharpening image ")..i.." ...")
-- create unique filename
new_name = output_folder..PS..df.get_basename(temp_name)..".jpg"
new_name = df.create_unique_filename(new_name)
-- build the GMic command string
input_file = df.sanitize_filename(temp_name)
output_file = df.sanitize_filename(new_name)
options = " cut 0,255 round "
if df.get_filetype(temp_name) == "tif" then options = " -/ 256"..options end
run_cmd = gmic.." "..input_file..gmic_operation..options.."o "..output_file..","..jpg_quality_str
result = dtsys.external_command(run_cmd)
if result ~= 0 then
dt.print(_("sharpening error"))
return
end
-- delete temp image
os.remove(temp_name)
end
dt.print(_("finished exporting"))
end
-- script_manager integration
local function destroy()
dt.destroy_storage("exp2RL")
end
-- new widgets ----------------------------------------------------------------
output_folder_selector = dt.new_widget("file_chooser_button"){
title = _("select output folder"),
tooltip = _("select output folder"),
value = dt.preferences.read(MODULE_NAME, "output_folder", "string"),
is_directory = true,
changed_callback = function(self)
dt.preferences.write(MODULE_NAME, "output_folder", "string", self.value)
end
}
sigma_slider = dt.new_widget("slider"){
label = _("sigma"),
tooltip = _("controls the width of the blur that's applied"),
soft_min = 0.3,
soft_max = 2.0,
hard_min = 0.0,
hard_max = 3.0,
step = 0.05,
digits = 2,
value = 1.0
}
iterations_slider = dt.new_widget("slider"){
label = _("iterations"),
tooltip = _("increase for better sharpening, but slower"),
soft_min = 0,
soft_max = 100,
hard_min = 0,
hard_max = 100,
step = 5,
digits = 0,
value = 10.0
}
jpg_quality_slider = dt.new_widget("slider"){
label = _("output jpg quality"),
tooltip = _("quality of the output jpg file"),
soft_min = 70,
soft_max = 100,
hard_min = 70,
hard_max = 100,
step = 2,
digits = 0,
value = 95.0
}
local storage_widget = dt.new_widget("box"){
orientation = "vertical",
output_folder_selector,
sigma_slider,
iterations_slider,
jpg_quality_slider
}
-- register new storage -------------------------------------------------------
dt.register_storage("exp2RL", _("RL output sharpen"), nil, export2RL, supported, save_preferences, storage_widget)
-- register the new preferences -----------------------------------------------
dt.preferences.register(MODULE_NAME, "gmic_exe", "file",
_("executable for GMic CLI"),
_("select executable for GMic command line version") , "")
-- set sliders to the last used value at startup ------------------------------
sigma_slider.value = dt.preferences.read(MODULE_NAME, "sigma", "float")
iterations_slider.value = dt.preferences.read(MODULE_NAME, "iterations", "float")
jpg_quality_slider.value = dt.preferences.read(MODULE_NAME, "jpg_quality", "float")
-- script_manager integration
script_data.destroy = destroy
return script_data
-- end of script --------------------------------------------------------------
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua;

View File

@@ -40,100 +40,19 @@ local darktable = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
local filelib = require "lib/dtutils.file" local filelib = require "lib/dtutils.file"
-- Forward declare the functions du.check_min_api_version("7.0.0", "autostyle")
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 -- return data structure for script_manager
du.check_min_api_version("2.0.2", "autostyle")
-- Receive the event triggered local script_data = {}
function autostyle_apply_one_image_event(event,image)
autostyle_apply_one_image(image)
end
-- Apply the style to an image, if it matches the tag condition script_data.destroy = nil -- function to destory the script
function autostyle_apply_one_image (image) script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
-- We need the tag, the value and the style_name provided from the configuration string script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
local tag,value,style_name=string.match(darktable.preferences.read("autostyle","exif_tag","string"),"(%g+)%s*=%s*(%g+)%s*=>%s*(%g+)") script_data.show = nil -- only required for libs since the destroy_method only hides them
-- check they all exist (correct syntax)
if (not tag) then
darktable.print("EXIF TAG not found in " .. darktable.preferences.read("autostyle","exif_tag","string"))
return 0
end
if (not value) then
darktable.print("value to match not found in " .. darktable.preferences.read("autostyle","exif_tag","string"))
return 0
end
if (not style_name) then
darktable.print("style name not found in " .. darktable.preferences.read("autostyle","exif_tag","string"))
return 0
end
if not filelib.check_if_bin_exists("exiftool") then
darktable.print("Can't find exiftool")
return 0
end
-- First find the style (we have its name)
local styles= darktable.styles
local style
for _,s in ipairs(styles) do
if s.name == style_name then
style=s
end
end
if (not style) then
darktable.print("style not found for autostyle: " .. style_name)
return 0
end
-- Apply the style to image, if it is tagged
local ok,auto_dr_attr= pcall(exiftool_attribute,image.path .. '/' .. image.filename,tag)
--darktable.print_error("dr_attr:" .. auto_dr_attr)
-- If the lookup fails, stop here
if (not ok) then
darktable.print("Couldn't get attribute " .. auto_dr_attr .. " from exiftool's output")
return 0
end
if auto_dr_attr==value then
darktable.print_log("Image " .. image.filename .. ": autostyle automatically applied " .. darktable.preferences.read("autostyle","exif_tag","string") )
darktable.styles.apply(style,image)
return 1
else
darktable.print_log("Image " .. image.filename .. ": autostyle not applied, exif tag " .. darktable.preferences.read("autostyle","exif_tag","string") .. " not matched: " .. auto_dr_attr)
return 0
end
end
function autostyle_apply( shortcut)
local images = darktable.gui.action_images
local images_processed =0
local images_submitted =0
for _,image in pairs(images) do
images_submitted = images_submitted +1
images_processed = images_processed + autostyle_apply_one_image(image)
end
darktable.print("Applied auto style to " .. images_processed .. " out of " .. images_submitted .. " image(s)")
end
-- Retrieve the attribute through exiftool
function exiftool_attribute(path,attr)
local cmd="exiftool -" .. attr .. " '" .. path .. "'";
local exifresult=get_stdout(cmd)
local attribute=string.match(exifresult,": (.*)")
if (attribute == nil) then
darktable.print_error( "Could not find the attribute " .. attr .. " using the command: <" .. cmd .. ">")
-- Raise an error to the caller
error( "Could not find the attribute " .. attr .. " using the command: <" .. cmd .. ">");
end
-- darktable.print_error("Returning attribute: " .. attribute)
return attribute
end
-- run command and retrieve stdout -- run command and retrieve stdout
function get_stdout(cmd) local function get_stdout(cmd)
-- Open the command, for reading -- Open the command, for reading
local fd = assert(io.popen(cmd, 'r')) local fd = assert(io.popen(cmd, 'r'))
darktable.control.read(fd) darktable.control.read(fd)
@@ -150,12 +69,108 @@ function get_stdout(cmd)
return data return data
end end
-- Retrieve the attribute through exiftool
local function exiftool_attribute(path, attr)
local cmd = "exiftool -" .. attr .. " '" .. path .. "'";
local exifresult = get_stdout(cmd)
local attribute = string.match(exifresult, ": (.*)")
if (attribute == nil) then
darktable.print_error( "Could not find the attribute " .. attr .. " using the command: <" .. cmd .. ">")
-- Raise an error to the caller
error( "Could not find the attribute " .. attr .. " using the command: <" .. cmd .. ">");
end
-- darktable.print_error("Returning attribute: " .. attribute)
return attribute
end
-- Apply the style to an image, if it matches the tag condition
local function autostyle_apply_one_image (image)
local pref = darktable.preferences.read("autostyle", "exif_tag", "string")
-- We need the tag, the value and the style_name provided from the configuration string
local tag, value, style_name = string.match(pref, "(%g+)%s*=%s*(%g+)%s*=>%s*(%g+)")
-- check they all exist (correct syntax)
if (not tag) then
darktable.print("EXIF TAG not found in " .. pref)
return 0
end
if (not value) then
darktable.print("value to match not found in " .. pref)
return 0
end
if (not style_name) then
darktable.print("style name not found in " .. pref)
return 0
end
if not filelib.check_if_bin_exists("exiftool") then
darktable.print("Can't find exiftool")
return 0
end
-- First find the style (we have its name)
local styles = darktable.styles
local style
for _, s in ipairs(styles) do
if s.name == style_name then
style = s
end
end
if (not style) then
darktable.print("style not found for autostyle: " .. style_name)
return 0
end
-- Apply the style to image, if it is tagged
local ok, auto_dr_attr = pcall(exiftool_attribute, image.path .. '/' .. image.filename,tag)
--darktable.print_error("dr_attr:" .. auto_dr_attr)
-- If the lookup fails, stop here
if (not ok) then
darktable.print("Couldn't get attribute " .. auto_dr_attr .. " from exiftool's output")
return 0
end
if auto_dr_attr == value then
darktable.print_log("Image " .. image.filename .. ": autostyle automatically applied " .. pref)
darktable.styles.apply(style,image)
return 1
else
darktable.print_log("Image " .. image.filename .. ": autostyle not applied, exif tag " .. pref .. " not matched: " .. auto_dr_attr)
return 0
end
end
-- Receive the event triggered
local function autostyle_apply_one_image_event(event,image)
autostyle_apply_one_image(image)
end
local function autostyle_apply(shortcut)
local images = darktable.gui.action_images
local images_processed = 0
local images_submitted = 0
for _,image in pairs(images) do
images_submitted = images_submitted + 1
images_processed = images_processed + autostyle_apply_one_image(image)
end
darktable.print("Applied auto style to " .. images_processed .. " out of " .. images_submitted .. " image(s)")
end
local function destroy()
darktable.destroy_event("autostyle", "shortcut")
darktable.destroy_event("autostyle", "post-import-image")
end
-- Registering events -- Registering events
darktable.register_event("shortcut",autostyle_apply, darktable.register_event("autostyle", "shortcut", autostyle_apply,
"Apply your chosen style from exiftool tags") "Apply your chosen style from exiftool tags")
darktable.preferences.register("autostyle","exif_tag","string","Autostyle: EXIF_tag=value=>style","apply a style automatically if an EXIF_tag matches value. Find the tag with exiftool","") darktable.preferences.register("autostyle", "exif_tag", "string", "Autostyle: EXIF_tag=value=>style", "apply a style automatically if an EXIF_tag matches value. Find the tag with exiftool", "")
darktable.register_event("post-import-image",autostyle_apply_one_image_event) darktable.register_event("autostyle", "post-import-image",
autostyle_apply_one_image_event)
script_data.destroy = destroy
return script_data

View File

@@ -0,0 +1,201 @@
--[[
change group leader
copyright (c) 2020, 2022 Bill Ferguson <wpferguson@gmail.com>
copyright (c) 2021 Angel Angelov
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/>.
]]
--[[
CHANGE GROUP LEADER
automatically change the leader of raw+jpg paired image groups
INSTALLATION
* copy this file in $CONFIGDIR/lua/ where CONFIGDIR is your darktable configuration directory
* add the following line in the file $CONFIGDIR/luarc
require "change_group_leader"
USAGE
* in lighttable mode, select the image groups you wish to process,
select whether you want to set the leader to "jpg" or "raw",
and click "Execute"
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local gettext = dt.gettext
local MODULE = "change_group_leader"
du.check_min_api_version("3.0.0", MODULE)
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain(MODULE, dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext(MODULE, msgid)
end
-- create a namespace to contain persistent data and widgets
chg_grp_ldr = {}
local cgl = chg_grp_ldr
cgl.widgets = {}
cgl.event_registered = false
cgl.module_installed = false
-- - - - - - - - - - - - - - - - - - - - - - - -
-- F U N C T I O N S
-- - - - - - - - - - - - - - - - - - - - - - - -
local function install_module()
if not cgl.module_installed then
dt.register_lib(
MODULE, -- Module name
_("change_group_leader"), -- Visible name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 700}}, -- containers
cgl.widgets.box,
nil,-- view_enter
nil -- view_leave
)
cgl.module_installed = true
end
end
local function find_group_leader(images, mode)
for _, img in ipairs(images) do
dt.print_log("checking image " .. img.id .. " named " .. img.filename)
local found = false
if mode == "jpg" then
if string.match(string.lower(img.filename), "jpg$") then
dt.print_log("jpg matched image " .. img.filename)
found = true
end
elseif mode == "raw" then
if img.is_raw and img.duplicate_index == 0 then
dt.print_log("found raw " .. img.filename)
found = true
end
elseif mode == "non-raw" then
if img.is_ldr then
dt.print_log("found ldr " .. img.filename)
found = true
end
else
dt.print_error(MODULE .. ": unrecognized mode " .. mode)
return
end
if found then
dt.print_log("making " .. img.filename .. " group leader")
img:make_group_leader()
return
end
end
end
local function process_image_groups(images)
if #images < 1 then
dt.print(_("No images selected."))
dt.print_log(MODULE .. "no images seletected, returning...")
else
local mode = cgl.widgets.mode.value
for _,img in ipairs(images) do
dt.print_log("checking image " .. img.id)
local group_images = img:get_group_members()
if group_images == 1 then
dt.print_log("only one image in group for image " .. image.id)
else
find_group_leader(group_images, mode)
end
end
end
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- S C R I P T M A N A G E R I N T E G R A T I O N
-- - - - - - - - - - - - - - - - - - - - - - - -
local function destroy()
dt.gui.libs[MODULE].visible = false
end
local function restart()
dt.gui.libs[MODULE].visible = true
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- W I D G E T S
-- - - - - - - - - - - - - - - - - - - - - - - -
cgl.widgets.mode = dt.new_widget("combobox"){
label = _("select new group leader"),
tooltip = _("select type of image to be group leader"),
selected = 1,
"jpg", "raw", "non-raw",
}
cgl.widgets.execute = dt.new_widget("button"){
label = _("Execute"),
clicked_callback = function()
process_image_groups(dt.gui.action_images)
end
}
cgl.widgets.box = dt.new_widget("box"){
orientation = "vertical",
cgl.widgets.mode,
cgl.widgets.execute,
}
-- - - - - - - - - - - - - - - - - - - - - - - -
-- D A R K T A B L E I N T E G R A T I O N
-- - - - - - - - - - - - - - - - - - - - - - - -
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not cgl.event_registered then
dt.register_event(
"view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
cgl.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data

View File

@@ -1,6 +1,6 @@
--[[ --[[
clear_GPS.lua - export and edit with GIMP clear_GPS.lua - plugin for Darktable
Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>. Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
@@ -38,13 +38,22 @@
local dt = require "darktable" local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local gettext = dt.gettext local gettext = dt.gettext
-- not a number -- not a number
local NaN = 0/0 local NaN = 0/0
du.check_min_api_version("3.0.0", "clear_GPS") du.check_min_api_version("7.0.0", "clear_GPS")
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("clear_GPS",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("clear_GPS",dt.configuration.config_dir.."/lua/locale/")
@@ -62,15 +71,23 @@ local function clear_GPS(images)
end end
end end
local function destroy()
dt.destroy_event("clear_GPS", "shortcut")
dt.gui.libs.image.destroy_action("clear_GPS")
end
script_data.destroy = destroy
dt.gui.libs.image.register_action( dt.gui.libs.image.register_action(
_("clear GPS data"), "clear_GPS", _("clear GPS data"),
function(event, images) clear_GPS(images) end, function(event, images) clear_GPS(images) end,
"clear GPS data" _("Clear GPS data from selected images")
) )
dt.register_event( dt.register_event(
"shortcut", "clear_GPS", "shortcut",
function(event, shortcut) clear_GPS(dt.gui.action_images) end, function(event, shortcut) clear_GPS(dt.gui.action_images) end,
"clear GPS data" _("Clear GPS data")
) )
return script_data

View File

@@ -0,0 +1,372 @@
--[[
color_profile_manager.lua - manage external darktable color profiles
Copyright (C) 2021 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/>.
]]
--[[
color_profile_manager - manage external darktable color profiles
This script provides a tool to manage input and output external color
profiles used by darktable. Color profiles can be added or removed
to/from the correct directories so that darktable can find and use
them.
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
* None
USAGE
* require this script from your main lua file
* select the input or output set of color profiles
* a list of profiles is displayed. Click the check box beside the
profile name to select it for removal. Click the "remove profile"
button to remove the profile.
* use the file selector to select a color profile to add to the currently
selected set (input or output)
* click the "add profile" button to add the profile to the selected set
BUGS, COMMENTS, SUGGESTIONS
* Send to Bill Ferguson, wpferguson@gmail.com or raise an issue on
https://github.com/dakrtable-org/lua-scripts
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
du.check_min_api_version("7.0.0", "color_profile_manager")
-- - - - - - - - - - - - - - - - - - - - - - - -
-- L O C A L I Z A T I O N
-- - - - - - - - - - - - - - - - - - - - - - - -
local gettext = dt.gettext
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("color_profile_manager", dt.configuration.config_dir .. "/lua/locale/")
local function _(msgid)
return gettext.dgettext("color_profile_manager", msgid)
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- C O N S T A N T S
-- - - - - - - - - - - - - - - - - - - - - - - -
local MODULE_NAME = "color_profile_manager"
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
local CS = dt.configuration.running_os == "windows" and "&" or ";"
local DIR_CMD = dt.configuration.running_os == "windows" and "dir /b" or "ls"
local COLOR_IN_DIR = dt.configuration.config_dir .. PS .. "color" .. PS .. "in"
local COLOR_OUT_DIR = dt.configuration.config_dir .. PS .. "color" .. PS .. "out"
-- - - - - - - - - - - - - - - - - - - - - - - -
-- N A M E S P A C E
-- - - - - - - - - - - - - - - - - - - - - - - -
local cpm = {}
cpm.widgets = {}
cpm.widgets.profile_box = dt.new_widget("box"){
orientation = "vertical",
}
cpm.module_installed = false
cpm.event_registered = false
-- - - - - - - - - - - - - - - - - - - - - - - -
-- F U N C T I O N S
-- - - - - - - - - - - - - - - - - - - - - - - -
local function check_for_directories()
if not df.test_file(COLOR_IN_DIR, "d") then
dt.print_log("didn't find " .. COLOR_IN_DIR)
return false
elseif not df.test_file(COLOR_OUT_DIR, "d") then
dt.print_log("didn't find " .. COLOR_OUT_DIR)
return false
else
dt.print_log("both directories exist")
return true
end
end
local function add_directories()
df.mkdir(COLOR_IN_DIR)
df.mkdir(COLOR_OUT_DIR)
end
local function list_profiles(dir)
local files = {}
local p = io.popen(DIR_CMD .. " " .. dir)
if p then
for line in p:lines() do
table.insert(files, line)
end
end
p:close()
return files
end
local function add_profile(file, dir)
df.file_copy(file, dir)
dt.print(_("added color profile " .. file .. " to " .. dir))
dt.print_log("color profile " .. file .. " added to " .. dir)
end
local function remove_profile(file, dir)
os.remove(dir .. PS .. file)
dt.print(_("removed color profile " .. file .. " from " .. dir))
dt.print_log("color profile " .. file .. " removed from " .. dir)
end
local function clear_profile_box()
for i = #cpm.widgets.profile_box, 1, -1 do
cpm.widgets.profile_box[i] = nil
end
end
local function add_profile_callback()
local profile_dir = COLOR_IN_DIR
if cpm.widgets.profile_set.selected == 2 then
profile_dir = COLOR_OUT_DIR
end
local new_profile = cpm.widgets.profile_selector.value
if string.lower(df.get_filetype(new_profile)) ~= "icm" and string.lower(df.get_filetype(new_profile)) ~= "icc" then
dt.print(_("ERROR: color profile must be an icc or icm file"))
dt.print_error(new_profile .. " selected and isn't an icc or icm file")
return
end
-- set selector value to directory that new profile came from
-- in case there are more
cpm.widgets.profile_selector.value = df.get_path(cpm.widgets.profile_selector.value)
add_profile(new_profile, profile_dir)
local files = list_profiles(profile_dir)
local widgets = {}
local profile_ptr = 1
for i, file in ipairs(files) do
if #cpm.widgets.profile_box == 0 or cpm.widgets.profile_box[profile_ptr].label ~= file then
table.insert(widgets, dt.new_widget("check_button"){value = false, label = file})
else
table.insert(widgets, cpm.widgets.profile_box[profile_ptr])
profile_ptr = profile_ptr + 1
if profile_ptr > #cpm.widgets.profile_box then
profile_ptr = #cpm.widgets.profile_box
end
end
end
clear_profile_box()
for _, widget in ipairs(widgets) do
table.insert(cpm.widgets.profile_box, widget)
end
if not cpm.widgets.remove_box.visible then
cpm.widgets.remove_box.visible = true
end
end
local function remove_profile_callback()
local widgets_to_keep = {}
local profile_dir = COLOR_IN_DIR
if cpm.widgets.profile_set.selected == 2 then
profile_dir = COLOR_OUT_DIR
end
for _, widget in ipairs(cpm.widgets.profile_box) do
if widget.value == true then
remove_profile(widget.label, profile_dir)
else
table.insert(widgets_to_keep, widget)
end
end
clear_profile_box()
for _, widget in ipairs(widgets_to_keep) do
table.insert(cpm.widgets.profile_box, widget)
end
if #cpm.widgets.profile_box == 0 then
cpm.widgets.remove_box.visible = false
else
cpm.widgets.remove_box.visible = true
end
end
local function list_profile_callback(choice)
local list_dir = COLOR_IN_DIR
if choice == 2 then
list_dir = COLOR_OUT_DIR
end
local files = list_profiles(list_dir)
if #files == 0 then
cpm.widgets.remove_box.visible = false
else
cpm.widgets.remove_box.visible = true
end
clear_profile_box()
for i, file in ipairs(files) do
cpm.widgets.profile_box[i] = dt.new_widget("check_button"){value = false, label = file}
end
end
local function install_module()
if not cpm.module_installed then
dt.register_lib(
MODULE_NAME, -- Module name
_("color profile manager"), -- Visible name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 0}}, -- containers
cpm.widgets.main_widget,
nil,-- view_enter
nil -- view_leave
)
cpm.module_installed = true
dt.control.sleep(500)
if not cpm.initialized then
cpm.widgets.profile_set.visible = false
cpm.widgets.add_box.visible = false
cpm.widgets.remove_box.visible = false
else
cpm.widgets.profile_set.selected = 1
end
end
end
local function destroy()
dt.gui.libs[MODULE_NAME].visible = false
return
end
local function restart()
dt.gui.libs[MODULE_NAME].visible = true
return
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- W I D G E T S
-- - - - - - - - - - - - - - - - - - - - - - - -
cpm.initialized = check_for_directories()
dt.print_log("cpm.initialized is " .. tostring(cpm.initialized))
if not cpm.initialized then
dt.print_log("creating init_box")
cpm.widgets.init_box = dt.new_widget("box"){
orientation = "vertical",
dt.new_widget("button"){
label = _("initialize color profiles"),
tooltip = _("create the directory structure to contain the color profiles"),
clicked_callback = function(this)
add_directories()
cpm.initialized = true
cpm.widgets.profile_set.visible = true
cpm.widgets.add_box.visible = true
cpm.widgets.remove_box.visible = false
cpm.widgets.init_box.visible = false
cpm.widgets.profile_set.selected = 1
end
}
}
end
cpm.widgets.profile_set = dt.new_widget("combobox"){
label = _("select profile set"),
tooltip = _("select input or output profiles"),
visible = cpm.initialized and false or true,
changed_callback = function(this)
if cpm.initialized then
list_profile_callback(this.selected)
end
end,
_("input"), _("output"),
}
cpm.widgets.profile_selector = dt.new_widget("file_chooser_button"){
title = _("select color profile to add"),
tooltip = _("select the .icc or .icm file to add"),
is_directory = false
}
cpm.widgets.add_box = dt.new_widget("box"){
orientation = "vertical",
visible = cpm.initialized and true or false,
dt.new_widget("section_label"){label = _("add profile")},
cpm.widgets.profile_selector,
dt.new_widget("button"){
label = _("add selected color profile"),
tooltip = _("add selected file to profiles"),
clicked_callback = function(this)
add_profile_callback()
end
}
}
cpm.widgets.remove_box = dt.new_widget("box"){
orientation = "vertical",
visible = cpm.initialized and true or false,
dt.new_widget("section_label"){label = _("remove profile")},
cpm.widgets.profile_box,
dt.new_widget("button"){
label = _("remove selected profile(s)"),
tooltip = _("remove the checked profile(s)"),
clicked_callback = function(this)
remove_profile_callback()
end
}
}
local main_widgets = {}
if not cpm.initialized then
table.insert(main_widgets, cpm.widgets.init_box)
end
table.insert(main_widgets, cpm.widgets.profile_set)
table.insert(main_widgets, cpm.widgets.remove_box)
table.insert(main_widgets, cpm.widgets.add_box)
cpm.widgets.main_widget = dt.new_widget("box"){
orientation = "vertical",
table.unpack(main_widgets)
}
-- - - - - - - - - - - - - - - - - - - - - - - -
-- M A I N
-- - - - - - - - - - - - - - - - - - - - - - - -
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not cpm.event_registered then
dt.register_event(
MODULE_NAME, "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
cpm.event_registered = true
end
end
local script_data = {}
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data

View File

@@ -42,7 +42,16 @@ local debug = require "darktable.debug"
local gettext = dt.gettext local gettext = dt.gettext
du.check_min_api_version("3.0.0", "copy_attach_detach_tags") du.check_min_api_version("7.0.0", "copy_attach_detach_tags")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- 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/") gettext.bindtextdomain("copy_attach_detach_tags",dt.configuration.config_dir.."/lua/locale/")
@@ -51,6 +60,11 @@ local function _(msgid)
return gettext.dgettext("copy_attach_detach_tags", msgid) return gettext.dgettext("copy_attach_detach_tags", msgid)
end end
local cadt = {}
cadt.module_installed = false
cadt.event_registered = false
cadt.widget_table = {}
local image_tags = {} local image_tags = {}
@@ -107,7 +121,7 @@ local function mcopy_tags()
local function attach_tags() local function attach_tags()
if next(image_tags) == nil then if next(image_tags) == nil then
dt.print(_('No tags to attached, please copy tags first.')) dt.print(_('No tag to attach, please copy tags first.'))
return true return true
end end
@@ -156,6 +170,61 @@ local function replace_tags()
dt.print(_('Tags replaced')) dt.print(_('Tags replaced'))
end end
local function install_module()
if not cadt.module_installed then
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(cadt.widget_table),
},
nil,
nil
)
cadt.module_installed = true
end
end
local function destroy()
dt.destroy_event("cadt_ct", "shortcut")
dt.destroy_event("cadt_at", "shortcut")
dt.destroy_event("cadt_dt", "shortcut")
dt.destroy_event("cadt_rt", "shortcut")
dt.gui.libs["tagging_addon"].visible = false
end
local function restart()
-- shortcut for copy
dt.register_event("cadt_ct", "shortcut",
mcopy_tags,
_('copy tags from selected image(s)'))
-- shortcut for attach
dt.register_event("cadt_at", "shortcut",
attach_tags,
_('paste tags to selected image(s)'))
-- shortcut for detaching tags
dt.register_event("cadt_dt", "shortcut",
detach_tags,
_('remove tags from selected image(s)'))
-- shortcut for replace tags
dt.register_event("cadt_rt", "shortcut",
replace_tags,
_('replace tags from selected image(s)'))
dt.gui.libs["tagging_addon"].visible = true
end
local function show()
dt.gui.libs["tagging_addon"].visible = true
end
-- create modul Tagging addons -- create modul Tagging addons
taglist_label.reset_callback = mcopy_tags taglist_label.reset_callback = mcopy_tags
@@ -169,73 +238,82 @@ local taglabel = dt.new_widget("label") {
local box1 = dt.new_widget("box"){ local box1 = dt.new_widget("box"){
orientation = "horizontal", orientation = "horizontal",
dt.new_widget("button") { dt.new_widget("button") {
label = _('multi copy tags'), label = _('multi copy tags'),
clicked_callback = mcopy_tags}, tooltip = _('copy tags from selected image(s)'),
clicked_callback = mcopy_tags},
dt.new_widget("button") { dt.new_widget("button") {
label = _('paste tags'), tooltip = _('paste tags to selected image(s)'),
clicked_callback = attach_tags} label = _('paste tags'),
clicked_callback = attach_tags}
} }
local box2 = dt.new_widget("box"){ local box2 = dt.new_widget("box"){
orientation = "horizontal", orientation = "horizontal",
dt.new_widget("button") { dt.new_widget("button") {
label = _('replace tags'), label = _('replace tags'),
clicked_callback = replace_tags}, tooltip = _('replace tags from selected image(s)'),
clicked_callback = replace_tags},
dt.new_widget("button") { dt.new_widget("button") {
label = _('remove all tags'), label = _('remove all tags'),
clicked_callback = detach_tags} tooltip = _('remove tags from selected image(s)'),
clicked_callback = detach_tags}
} }
local sep = dt.new_widget("separator"){} local sep = dt.new_widget("separator"){}
-- pack elements into widget table for a nicer layout -- pack elements into widget table for a nicer layout
local widget_table = {}
widget_table[1] = box1 cadt.widget_table[1] = box1
widget_table[#widget_table+1] = box2 cadt.widget_table[#cadt.widget_table+1] = box2
widget_table[#widget_table+1] = sep cadt.widget_table[#cadt.widget_table+1] = sep
widget_table[#widget_table+1] = taglabel cadt.widget_table[#cadt.widget_table+1] = taglabel
widget_table[#widget_table+1] = taglist_label cadt.widget_table[#cadt.widget_table+1] = taglist_label
-- create modul -- create modul
dt.register_lib("tagging_addon","Tagging addon",true,true,{ if dt.gui.current_view().id == "lighttable" then
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",500} install_module()
}, else
dt.new_widget("box") { if not cadt.event_registered then
-- orientation = "vertical", dt.register_event(
reset_callback = function() "cadt", "view-changed",
taglist_label.label = "" function(event, old_view, new_view)
image_tags = {} if new_view.name == "lighttable" and old_view.name == "darkroom" then
end, install_module()
table.unpack(widget_table), end
}, end
nil, )
nil cadt.event_registered = true
) end
end
-- shortcut for copy -- shortcut for copy
dt.register_event("shortcut", dt.register_event("cadt_ct", "shortcut",
mcopy_tags, mcopy_tags,
_('copy tags from selected image(s)')) _('copy tags from selected image(s)'))
-- shortcut for attach -- shortcut for attach
dt.register_event("shortcut", dt.register_event("cadt_at", "shortcut",
attach_tags, attach_tags,
_('paste tags to selected image(s)')) _('paste tags to selected image(s)'))
-- shortcut for detaching tags -- shortcut for detaching tags
dt.register_event("shortcut", dt.register_event("cadt_dt", "shortcut",
detach_tags, detach_tags,
_('remove tags from selected image(s)')) _('remove tags from selected image(s)'))
-- shortcut for replace tags -- shortcut for replace tags
dt.register_event("shortcut", dt.register_event("cadt_rt", "shortcut",
replace_tags, replace_tags,
_('replace tags from selected image(s)')) _('replace tags from selected image(s)'))
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = show
return script_data
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on; -- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;

View File

@@ -35,8 +35,16 @@ USAGE
local darktable = require "darktable" local darktable = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
-- Tested with darktable 2.0.1 du.check_min_api_version("7.0.0", "cr2hdr")
du.check_min_api_version("2.0.0", "cr2hdr")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local queue = {} local queue = {}
local processed_files = {} local processed_files = {}
@@ -107,8 +115,21 @@ local function convert_action_images(shortcut)
convert_images() convert_images()
end end
darktable.register_event("shortcut", convert_action_images, "Run cr2hdr (Magic Lantern DualISO converter) on selected images") local function destroy()
darktable.register_event("post-import-image", file_imported) darktable.destroy_event("cr2hdr", "shortcut")
darktable.register_event("post-import-film", film_imported) darktable.destroy_event("cr2hdr", "post-import-image")
darktable.destroy_event("cr2hdr", "post-import-film")
end
darktable.register_event("cr2hdr", "shortcut",
convert_action_images, "Run cr2hdr (Magic Lantern DualISO converter) on selected images")
darktable.register_event("cr2hdr", "post-import-image",
file_imported)
darktable.register_event("cr2hdr", "post-import-film",
film_imported)
darktable.preferences.register("cr2hdr", "onimport", "bool", "Invoke on import", "If true then cr2hdr will try to proccess every file during importing. Warning: cr2hdr is quite slow even in figuring out on whether the file is Dual ISO or not.", false) darktable.preferences.register("cr2hdr", "onimport", "bool", "Invoke on import", "If true then cr2hdr will try to proccess every file during importing. Warning: cr2hdr is quite slow even in figuring out on whether the file is Dual ISO or not.", false)
script_data.destroy = destroy
return script_data

View File

@@ -26,10 +26,11 @@ enfuse ver. 4.2 or greater
exiftool exiftool
----USAGE---- ----USAGE----
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts ) Install:
1) Copy this file in to your 'lua/contrib' folder where all other scripts reside. 1) Get the Lua scripts: https://github.com/darktable-org/lua-scripts#download-and-install
2) Require this file in your luarc file, as with any other dt plug-in 2) Require this file in your luarc file, as with any other dt plug-in: require "contrib/enfuseAdvanced"
On the initial startup go to darktable settings > lua options and set your executable paths and other preferences, then restart darktable 3) Then select "DRI or DFF image" as storage option
4) On the initial startup set your executable paths
DRI = Dynamic Range Increase (Blend multiple bracket images into a single LDR file) DRI = Dynamic Range Increase (Blend multiple bracket images into a single LDR file)
DFF = Depth From Focus ('Focus Stacking' - Blend multiple images with different focus into a single image) DFF = Depth From Focus ('Focus Stacking' - Blend multiple images with different focus into a single image)
@@ -64,7 +65,16 @@ local mod = 'module_enfuseAdvanced'
local os_path_seperator = '/' local os_path_seperator = '/'
if dt.configuration.running_os == 'windows' then os_path_seperator = '\\' end if dt.configuration.running_os == 'windows' then os_path_seperator = '\\' end
du.check_min_api_version('5.0.0', 'enfuseAdvanced') du.check_min_api_version("7.0.0", "enfuseAdvanced")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
local gettext = dt.gettext local gettext = dt.gettext
@@ -179,6 +189,11 @@ for _,i in pairs(dt.styles) do
end end
-- FUNCTION -- -- FUNCTION --
local function sanitize_decimals(cmd) -- make sure decimal separator is a '.'
return string.gsub(cmd, '(%d),(%d)', "%1.%2")
end
local function InRange(test, low, high) -- tests if test value is within range of low and high (inclusive) local function InRange(test, low, high) -- tests if test value is within range of low and high (inclusive)
if test >= low and test <= high then if test >= low and test <= high then
return true return true
@@ -239,7 +254,7 @@ end
local function ExeUpdate(prog_tbl) --updates executable paths and verifies them local function ExeUpdate(prog_tbl) --updates executable paths and verifies them
dt.preferences.write(mod, 'bin_exists', 'bool', true) dt.preferences.write(mod, 'bin_exists', 'bool', true)
for _,prog in pairs(prog_tbl) do for x,prog in pairs(prog_tbl) do
dt.preferences.write('executable_paths', prog.name, 'string', GUI.exes[prog.name].value) dt.preferences.write('executable_paths', prog.name, 'string', GUI.exes[prog.name].value)
prog.bin = df.check_if_bin_exists(prog.name) prog.bin = df.check_if_bin_exists(prog.name)
if not prog.bin then if not prog.bin then
@@ -310,7 +325,7 @@ local function UpdateENFargs(image_table, prefix) --updates the Enfuse arguments
local largest_id = 0 local largest_id = 0
local first_raw = {} local first_raw = {}
for raw, temp_image in pairs(image_table) do for raw, temp_image in pairs(image_table) do
_, source_name, source_id = GetFileName(raw.filename) local _, source_name, source_id = GetFileName(raw.filename)
source_id = tonumber(source_id) source_id = tonumber(source_id)
if source_id < smallest_id then if source_id < smallest_id then
smallest_id = source_id smallest_id = source_id
@@ -434,6 +449,9 @@ local function main(storage, image_table, extra_data)
local job = dt.gui.create_job('aligning images') local job = dt.gui.create_job('aligning images')
image_table, images_to_remove, AIS.images_string = UpdateAISargs(image_table, images_to_remove) image_table, images_to_remove, AIS.images_string = UpdateAISargs(image_table, images_to_remove)
local run_cmd = BuildExecuteCmd(AIS) local run_cmd = BuildExecuteCmd(AIS)
dt.print_log("AIS run command is " .. run_cmd)
run_cmd = sanitize_decimals(run_cmd)
dt.print_log("AIS decimaal sanitized command is " .. run_cmd)
local resp = dsys.external_command(run_cmd) local resp = dsys.external_command(run_cmd)
job.valid = false job.valid = false
if resp ~= 0 then if resp ~= 0 then
@@ -454,11 +472,14 @@ local function main(storage, image_table, extra_data)
local image_num = 0 local image_num = 0
local job = dt.gui.create_job('blending '..#variants..' image(s)', true) --create a GUI job bar to display enfuse progress local job = dt.gui.create_job('blending '..#variants..' image(s)', true) --create a GUI job bar to display enfuse progress
UpdateActivePreference() --load current GUI values into active preference (only applies to elements without a clicked/changed callback) UpdateActivePreference() --load current GUI values into active preference (only applies to elements without a clicked/changed callback)
for _,prefix in pairs(variants) do --for each image to be created load in the preference values, build arguments string, output image, and run command then execute. for x,prefix in pairs(variants) do --for each image to be created load in the preference values, build arguments string, output image, and run command then execute.
job.percent = image_num /(#variants) job.percent = image_num /(#variants)
image_num = image_num+1 image_num = image_num+1
ENF.images_string, final_image, source_raw = UpdateENFargs(image_table, prefix) ENF.images_string, final_image, source_raw = UpdateENFargs(image_table, prefix)
local run_cmd = BuildExecuteCmd(ENF) local run_cmd = BuildExecuteCmd(ENF)
dt.print_log("ENF run command is " .. run_cmd)
run_cmd = sanitize_decimals(run_cmd)
dt.print_log("ENF decimaal sanitized command is " .. run_cmd)
local resp = dsys.external_command(run_cmd) local resp = dsys.external_command(run_cmd)
if resp ~= 0 then if resp ~= 0 then
remove_temp_files(images_to_remove) remove_temp_files(images_to_remove)
@@ -469,11 +490,16 @@ local function main(storage, image_table, extra_data)
--copy exif data from original file --copy exif data from original file
run_cmd = EXF.bin..' -TagsFromFile '..df.sanitize_filename(source_raw.path..os_path_seperator..source_raw.filename)..' -exif:all --subifd:all -overwrite_original '..df.sanitize_filename(final_image) run_cmd = EXF.bin..' -TagsFromFile '..df.sanitize_filename(source_raw.path..os_path_seperator..source_raw.filename)..' -exif:all --subifd:all -overwrite_original '..df.sanitize_filename(final_image)
-- replace comma decimal separator with period
dt.print_log("EXF run command is " .. run_cmd)
run_cmd = sanitize_decimals(run_cmd)
dt.print_log("EXF decimaal sanitized command is " .. run_cmd)
resp = dsys.external_command(run_cmd) resp = dsys.external_command(run_cmd)
if GUI.Target.auto_import.value then --import image into dt if specified if GUI.Target.auto_import.value then --import image into dt if specified
local imported = dt.database.import(final_image) local imported = dt.database.import(final_image)
dt.print_log("image imported")
if GUI.Target.apply_style.selected > 1 then --apply specified style to imported image if GUI.Target.apply_style.selected > 1 then --apply specified style to imported image
local set_style = styles[GUI.Target.apply_style.selected - 1] local set_style = styles[GUI.Target.apply_style.selected - 1]
dt.styles.apply(set_style , imported) dt.styles.apply(set_style , imported)
@@ -499,6 +525,10 @@ local function main(storage, image_table, extra_data)
dt.print('image fusion process complete') dt.print('image fusion process complete')
end end
local function destroy()
dt.destroy_storage('module_enfuseAdvanced')
end
--GUI-- --GUI--
stack_compression = dt.new_widget('stack'){} stack_compression = dt.new_widget('stack'){}
local label_AIS_options= dt.new_widget('section_label'){ local label_AIS_options= dt.new_widget('section_label'){
@@ -938,7 +968,7 @@ GUI.Presets.current_preset = dt.new_widget('combobox'){
end end
} }
GUI.Presets.load = dt.new_widget('button'){ GUI.Presets.load = dt.new_widget('button'){
label = _('laod fusion preset'), label = _('load fusion preset'),
tooltip = _('load current fusion parameters from selected preset'), tooltip = _('load current fusion parameters from selected preset'),
clicked_callback = function() LoadFromPreference(GUI.Presets.current_preset.value) end clicked_callback = function() LoadFromPreference(GUI.Presets.current_preset.value) end
} }
@@ -1125,3 +1155,7 @@ else
GUI.options_contain.active = 4 GUI.options_contain.active = 4
GUI.show_options.sensitive = false GUI.show_options.sensitive = false
end end
script_data.destroy = destroy
return script_data

199
contrib/exportLUT.lua Normal file
View File

@@ -0,0 +1,199 @@
--[[
This file is part of darktable,
copyright (c) 2020 Noah Clarke
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 the following line to .config/darktable/luarc to enable this lightable module:
require "contrib/exportLut"
Given a haldCLUT identity file this script generates haldCLUTS from all the user's
styles and exports them to a location of their choosing.
Warning: during export if a naming collision occurs the older file is automatically
overwritten silently.
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require("lib/dtutils.file")
local ds = require("lib/dtutils.system")
du.check_min_api_version("7.0.0", "exportLUT")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local gettext = dt.gettext
gettext.bindtextdomain("exportLUT",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("exportLUT", msgid)
end
du.check_min_api_version("5.0.0", "exportLUT")
local eL = {}
eL.module_installed = false
eL.event_registered = false
eL.widgets = {}
-- Thanks Kevin Ertel for this bit
local os_path_seperator = '/'
if dt.configuration.running_os == 'windows' then os_path_seperator = '\\' end
local mkdir_command = 'mkdir -p '
if dt.configuration.running_os == 'windows' then mkdir_command = 'mkdir ' end
local file_chooser_button = dt.new_widget("file_chooser_button"){
title = _("Identity_file_chooser"),
value = "",
is_directory = false
}
local export_chooser_button = dt.new_widget("file_chooser_button"){
title = _("Export_location_chooser"),
value = "",
is_directory = true
}
local identity_label = dt.new_widget("label"){
label = _("choose the identity haldclut file")
}
local output_label = dt.new_widget("label"){
label = _("choose the output location")
}
local warning_label = dt.new_widget("label"){
label = _("WARNING: files may be silently overwritten")
}
local function end_job(job)
job.valid = false
end
local function output_path(style_name, job)
local output_location = export_chooser_button.value .. os_path_seperator .. style_name .. ".png"
output_location = string.gsub(output_location, "|", os_path_seperator)
local output_dir = string.reverse(output_location)
output_dir = string.gsub(output_dir, ".-" .. os_path_seperator, os_path_seperator, 1)
output_dir = string.reverse(output_dir)
if(output_dir ~= "") then
df.mkdir(df.sanitize_filename(output_dir))
end
return output_location
end
local function export_luts()
local identity = dt.database.import(file_chooser_button.value)
if(type(identity) ~= "userdata") then
dt.print(_("Invalid identity lut file"))
else
local job = dt.gui.create_job(_('Exporting styles as haldCLUTs'), true, end_job)
local size = 1
for style_num, style in ipairs(dt.styles) do
size = size + 1
end
local count = 0
for style_num, style in ipairs(dt.styles) do
identity:reset()
dt.styles.apply(style, identity)
local io_lut = dt.new_format("png")
io_lut.bpp = 8
io_lut:write_image(identity, output_path(style.name, job))
count = count + 1
job.percent = count / size
dt.print(_("Exported: ") .. output_path(style.name, job))
end
dt.print(_("Done exporting haldCLUTs"))
job.valid = false
identity:reset()
end
end
local function install_module()
if not eL.module_installed then
dt.register_lib(
"export haldclut",
_("export haldclut"),
true,
false,
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}},
dt.new_widget("box")
{
orientation = "vertical",
table.unpack(eL.widgets),
},
nil,
nil
)
eL.module_installed = true
end
end
local function destroy()
dt.gui.libs["export haldclut"].visible = false
end
local function restart()
dt.gui.libs["export haldclut"].visible = true
end
local export_button = dt.new_widget("button"){
label = _("export"),
clicked_callback = export_luts
}
table.insert(eL.widgets, identity_label)
table.insert(eL.widgets, file_chooser_button)
table.insert(eL.widgets, output_label)
table.insert(eL.widgets, export_chooser_button)
table.insert(eL.widgets, warning_label)
table.insert(eL.widgets, export_button)
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not eL.event_registered then
dt.register_event(
"exportLUT", "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
eL.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data

508
contrib/ext_editor.lua Normal file
View File

@@ -0,0 +1,508 @@
--[[
ext_editor.lua - edit images with external editors
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/>.
]]
--[[
ext_editor.lua - edit images with external editors
This script provides helpers to edit image files with programs external to darktable. It adds:
- a new target storage "collection". Image exported will be reimported to collection for further edit with external programs
- a new module "external editors", visible in lightable and darkroom, to select a program from a list
- of up to 9 external editors and run it on a selected image (adjust this limit by changing MAX_EDITORS)
- a set of lua preferences in order to configure name and path of up to 9 external editors
- a set of lua shortcuts in order to quick launch the external editors
USAGE
* require this script from main lua file
-- setup --
* in "preferences/lua options" configure name and path/command of external programs
* note that if a program name is left empty, that and all following entries will be ignored
* in "preferences/shortcuts/lua" configure shortcuts for external programs (optional)
* whenever programs preferences are changed, in external editors GUI, press "update list"
-- use --
* in the export dialog choose "collection" and select the format and bit depth for the
exported image
* press "export"
* the exported image will be imported into collection and grouped with the original image
* in lighttable, select an image for editing with en external program
* (or in darkroom for the image being edited):
* in external editors GUI, select program and press "edit"
* edit the image with the external editor, overwite the file, quit the external program
* the selected image will be updated
or
* in external editors GUI, select program and press "edit a copy"
* edit the image with the external editor, overwite the file, quit the external program
* a copy of the selected image will be created and updated
or
* use the shortcut to edit the current image with the corresponding external editor
* overwite the file, quit the external program
* the image will be updated
* warning: mouseover on lighttable/filmstrip will prevail on current image
* this is the default DT behavior, not a bug of this script
CAVEATS
* MAC compatibility not tested
BUGS, COMMENTS, SUGGESTIONS
* send to Marco Carrarini, marco.carrarini@gmail.com
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local dtsys = require "lib/dtutils.system"
-- module name
local MODULE_NAME = "ext_editor"
du.check_min_api_version("7.0.0", MODULE_NAME)
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- OS compatibility
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
-- namespace
local ee = {}
ee.module_installed = false
ee.event_registered = false
ee.widgets = {}
-- translation
local gettext = dt.gettext
gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
local function _(msgid)
return gettext.dgettext(MODULE_NAME, msgid)
end
-- maximum number of external programs, can be increased to necessity
local MAX_EDITORS = 9
-- number of valid entries in the list of external programs
local n_entries
-- allowed file extensions for external editors
local allowed_file_types = {"JPG", "jpg", "JPEG", "jpeg", "TIF", "tif", "TIFF", "tiff", "EXR", "exr", "PNG", "png"}
-- last used editor initialization
if not dt.preferences.read(MODULE_NAME, "initialized", "bool") then
dt.preferences.write(MODULE_NAME, "lastchoice", "integer", 0)
dt.preferences.write(MODULE_NAME, "initialized", "bool", true)
end
local lastchoice = 0
-- update lists of program names and paths, as well as combobox ---------------
local function UpdateProgramList(combobox, button_edit, button_edit_copy, update_button_pressed)
-- initialize lists
program_names = {}
program_paths = {}
-- build lists from preferences
local name
local last = false
n_entries = 0
for i = 1, MAX_EDITORS do
name = dt.preferences.read(MODULE_NAME, "program_name_"..i, "string")
if (name == "" or name == nil) then last = true end
if last then
if combobox[n_entries + 1] then combobox[n_entries + 1] = nil end -- remove extra combobox entries
else
combobox[i] = i..": "..name
program_names[i] = name
program_paths[i] = df.sanitize_filename(dt.preferences.read(MODULE_NAME, "program_path_"..i, "string"))
n_entries = i
end
end
lastchoice = dt.preferences.read(MODULE_NAME, "lastchoice", "integer")
if lastchoice == 0 and n_entries > 0 then lastchoice = 1 end
if lastchoice > n_entries then lastchoice = n_entries end
dt.preferences.write(MODULE_NAME, "lastchoice", "integer", lastchoice)
-- widgets enabled if there is at least one program configured
combobox.selected = lastchoice
local active = n_entries > 0
combobox.sensitive = active
button_edit.sensitive = active
button_edit_copy.sensitive = active
if update_button_pressed then dt.print(n_entries.._(" editors configured")) end
end
-- callback for buttons "edit" and "edit a copy" ------------------------------
local function OpenWith(images, choice, copy)
-- check choice is valid, return if not
if choice > n_entries then
dt.print(_("not a valid choice"))
return
end
-- check if one image is selected, return if not
if #images ~= 1 then
dt.print(_("please select one image"))
return
end
local bin = program_paths[choice]
local friendly_name = program_names[choice]
if dt.configuration.running_os == "macos" then bin = "open -W -a "..bin end
-- image to be edited
local image
i, image = next(images)
local name = image.path..PS..image.filename
-- check if image format is allowed
local file_ext = df.get_filetype(image.filename)
local allowed = false
for i,v in pairs(allowed_file_types) do
if v == file_ext then
allowed = true
break
end
end
if not allowed then
dt.print(_("file type not allowed"))
return
end
-- save image tags, rating and color
local tags = {}
for i, tag in ipairs(dt.tags.get_tags(image)) do
if not (string.sub(tag.name, 1, 9) == "darktable") then table.insert(tags, tag) end
end
local rating = image.rating
local red = image.red
local blue = image.blue
local green = image.green
local yellow = image.yellow
local purple = image.purple
-- new image
local new_name = name
local new_image = image
if copy then
-- create unique filename
new_name = df.create_unique_filename(new_name)
-- physical copy, check result, return if error
local copy_success = df.file_copy(name, new_name)
if not copy_success then
dt.print(_("error copying file ")..name)
return
end
end
-- launch the external editor, check result, return if error
local run_cmd = bin.." "..df.sanitize_filename(new_name)
dt.print(_("launching ")..friendly_name.."...")
local result = dtsys.external_command(run_cmd)
if result ~= 0 then
dt.print(_("error launching ")..friendly_name)
return
end
if copy then
-- import in database and group
new_image = dt.database.import(new_name)
new_image:group_with(image)
else
-- refresh the image view
-- note that only image:drop_cache() is not enough to refresh view in darkroom mode
-- therefore image must be deleted and reimported to force refresh
-- find the grouping status
local image_leader = image.group_leader
local group_members = image:get_group_members()
local new_leader
local index = nil
local found = false
-- membership status, three different cases
if image_leader == image then
if #group_members > 1 then
-- case 1: image is leader in a group with more members
while not found do
index, new_leader = next(group_members, index)
if new_leader ~= image_leader then found = true end
end
new_leader:make_group_leader()
image:delete()
if image.local_copy then image:drop_cache() end -- to fix fail to allocate cache error
new_image = dt.database.import(name)
new_image:group_with(new_leader)
new_image:make_group_leader()
else
-- case 2: image is the only member in group
image:delete()
if image.local_copy then image:drop_cache() end -- to fix fail to allocate cache error
new_image = dt.database.import(name)
new_image:group_with()
end
else
-- case 3: image is in a group but is not leader
image:delete()
if image.local_copy then image:drop_cache() end -- to fix fail to allocate cache error
new_image = dt.database.import(name)
new_image:group_with(image_leader)
end
end
-- restore image tags, rating and color
for i, tag in ipairs(tags) do dt.tags.attach(tag, new_image) end
new_image.rating = rating
new_image.red = red
new_image.blue = blue
new_image.green = green
new_image.yellow = yellow
new_image.purple = purple
-- select the new image
local selection = {}
table.insert(selection, new_image)
dt.gui.selection(selection)
-- refresh darkroom view
if dt.gui.current_view().id == "darkroom" then
dt.gui.views.darkroom.display_image(new_image)
end
end
-- callback function for shortcuts --------------------------------------------
local function program_shortcut(event, shortcut)
OpenWith(dt.gui.action_images, tonumber(string.sub(shortcut, -2)), false)
end
-- export images and reimport in collection -----------------------------------
local function export2collection(storage, image_table, extra_data)
local temp_name, new_name, new_image, move_success
for image, temp_name in pairs(image_table) do
-- images are first exported in temp folder then moved to collection folder
-- create unique filename
new_name = image.path..PS..df.get_filename(temp_name)
new_name = df.create_unique_filename(new_name)
-- move image to collection folder, check result, return if error
move_success = df.file_move(temp_name, new_name)
if not move_success then
dt.print(_("error moving file ")..temp_name)
return
end
-- import in database and group
new_image = dt.database.import(new_name)
new_image:group_with(image.group_leader)
end
dt.print(_("finished exporting"))
end
-- install the module in the UI -----------------------------------------------
local function install_module(dr)
local views = {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 90}}
if dr then
views = {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 90},
[dt.gui.views.darkroom] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 100}}
end
if not ee.module_installed then
-- register new module "external editors" in lighttable and darkroom ----
dt.register_lib(
MODULE_NAME,
_("external editors"),
true, -- expandable
false, -- resetable
views,
dt.new_widget("box") {
orientation = "vertical",
table.unpack(ee.widgets),
},
nil, -- view_enter
nil -- view_leave
)
ee.module_installed = true
end
end
local function destroy()
for i = 1, MAX_EDITORS do
dt.destroy_event(MODULE_NAME .. i, "shortcut")
end
dt.destroy_storage("exp2coll")
dt.gui.libs[MODULE_NAME].visible = false
end
local function restart()
for i = 1, MAX_EDITORS do
dt.register_event(MODULE_NAME .. i, "shortcut",
program_shortcut, _("edit with program ")..string.format("%02d", i))
end
dt.register_storage("exp2coll", _("collection"), nil, export2collection)
dt.gui.libs[MODULE_NAME].visible = true
end
local function show()
dt.gui.libs[MODULE_NAME].visible = true
end
-- combobox, with variable number of entries ----------------------------------
local combobox = dt.new_widget("combobox") {
label = _("choose program"),
tooltip = _("select the external editor from the list"),
changed_callback = function(self)
dt.preferences.write(MODULE_NAME, "lastchoice", "integer", self.selected)
end,
""
}
-- button edit ----------------------------------------------------------------
local button_edit = dt.new_widget("button") {
label = _("edit"),
tooltip = _("open the selected image in external editor"),
--sensitive = false,
clicked_callback = function()
OpenWith(dt.gui.action_images, combobox.selected, false)
end
}
-- button edit a copy ---------------------------------------------------------
local button_edit_copy = dt.new_widget("button") {
label = _("edit a copy"),
tooltip = _("create a copy of the selected image and open it in external editor"),
clicked_callback = function()
OpenWith(dt.gui.action_images, combobox.selected, true)
end
}
-- button update list ---------------------------------------------------------
local button_update_list = dt.new_widget("button") {
label = _("update list"),
tooltip = _("update list of programs if lua preferences are changed"),
clicked_callback = function()
UpdateProgramList(combobox, button_edit, button_edit_copy, true)
end
}
-- box for the buttons --------------------------------------------------------
-- it doesn't seem there is a way to make the buttons equal in size
local box1 = dt.new_widget("box") {
orientation = "horizontal",
button_edit,
button_edit_copy,
button_update_list
}
-- table with all the widgets --------------------------------------------------
table.insert(ee.widgets, combobox)
table.insert(ee.widgets, box1)
-- register new module, but only when in lighttable ----------------------------
local show_dr = dt.preferences.read(MODULE_NAME, "show_in_darkrooom", "bool")
if dt.gui.current_view().id == "lighttable" then
install_module(show_dr)
else
if not ee.event_registered then
dt.register_event(
MODULE_NAME, "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module(show_dr)
end
end
)
ee.event_registered = true
end
end
-- initialize list of programs and widgets ------------------------------------
UpdateProgramList(combobox, button_edit, button_edit_copy, false)
-- register new storage -------------------------------------------------------
dt.register_storage("exp2coll", _("collection"), nil, export2collection)
-- register the new preferences -----------------------------------------------
for i = MAX_EDITORS, 1, -1 do
dt.preferences.register(MODULE_NAME, "program_path_"..i, "file",
_("executable for external editor ")..i,
_("select executable for external editor") , _("(None)"))
dt.preferences.register(MODULE_NAME, "program_name_"..i, "string",
_("name of external editor ")..i,
_("friendly name of external editor"), "")
end
dt.preferences.register(MODULE_NAME, "show_in_darkrooom", "bool",
_("show external editors in darkroom"),
_("check to show external editors module also in darkroom (requires restart)"), false)
-- register the new shortcuts -------------------------------------------------
for i = 1, MAX_EDITORS do
dt.register_event(MODULE_NAME .. i, "shortcut",
program_shortcut, _("edit with program ")..string.format("%02d", i))
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = show
return script_data
-- end of script --------------------------------------------------------------
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua;

View File

@@ -52,32 +52,28 @@ local MODULE = "face_recognition"
local PS = dt.configuration.running_os == "windows" and '\\' or '/' local PS = dt.configuration.running_os == "windows" and '\\' or '/'
local OUTPUT = dt.configuration.tmp_dir .. PS .. "facerecognition.txt" local OUTPUT = dt.configuration.tmp_dir .. PS .. "facerecognition.txt"
du.check_min_api_version("7.0.0", MODULE)
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- namespace -- namespace
local fc = {} local fc = {}
fc.module_installed = false
-- ensure we meet the minimum api fc.event_registered = false
du.check_min_api_version("5.0.0", "face_recognition")
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("face_recognition", dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("face_recognition", dt.configuration.config_dir.."/lua/locale/")
local function _(msgid) local function _(msgid)
return gettext.dgettext("face_recognition", msgid) return gettext.dgettext("face_recognition", msgid)
end
-- preferences
if not dt.preferences.read(MODULE, "initialized", "bool") then
dt.preferences.write(MODULE, "unknown_tag", "string", "unknown_person")
dt.preferences.write(MODULE, "ignore_tags", "string", "")
dt.preferences.write(MODULE, "tolerance", "float", 0.6)
dt.preferences.write(MODULE, "num_cores", "integer", 0)
dt.preferences.write(MODULE, "known_image_path", "directory", dt.configuration.config_dir .. "/face_recognition")
dt.preferences.write(MODULE, "export_format", "integer", 1)
dt.preferences.write(MODULE, "max_width", "integer", 1000)
dt.preferences.write(MODULE, "max_height", "integer", 1000)
dt.preferences.write(MODULE, "initialized", "bool", true)
end end
local function build_image_table(images) local function build_image_table(images)
@@ -98,8 +94,10 @@ local function build_image_table(images)
end end
for _,img in ipairs(images) do for _,img in ipairs(images) do
image_table[img] = tmp_dir .. df.get_basename(img.filename) .. file_extension if img ~= nil then
cnt = cnt + 1 image_table[tmp_dir .. df.get_basename(img.filename) .. file_extension] = img
cnt = cnt + 1
end
end end
return image_table, cnt return image_table, cnt
@@ -109,17 +107,12 @@ local function stop_job(job)
job.valid = false job.valid = false
end end
local function do_export(img_tbl) local function do_export(img_tbl, images)
local exporter = nil local exporter = nil
local upsize = false local upsize = false
local upscale = false
local ff = fc.export_format.value local ff = fc.export_format.value
local height = dt.preferences.read(MODULE, "max_height", "integer") local height = dt.preferences.read(MODULE, "max_height", "integer")
local width = dt.preferences.read(MODULE, "max_width", "integer") local width = dt.preferences.read(MODULE, "max_width", "integer")
local images = 0
for k,v in pairs(img_tbl) do
images = images + 1
end
-- get the export format parameters -- get the export format parameters
if string.match(ff, "JPEG") then if string.match(ff, "JPEG") then
@@ -140,7 +133,7 @@ local function do_export(img_tbl)
local exp_cnt = 0 local exp_cnt = 0
local percent_step = 1.0 / images local percent_step = 1.0 / images
job.percent = 0.0 job.percent = 0.0
for img,export in pairs(img_tbl) do for export,img in pairs(img_tbl) do
exp_cnt = exp_cnt + 1 exp_cnt = exp_cnt + 1
dt.print(string.format(_("Exporting image %i of %i images"), exp_cnt, images)) dt.print(string.format(_("Exporting image %i of %i images"), exp_cnt, images))
exporter:write_image(img, export, upsize) exporter:write_image(img, export, upsize)
@@ -154,13 +147,31 @@ end
local function save_preferences() local function save_preferences()
dt.preferences.write(MODULE, "unknown_tag", "string", fc.unknown_tag.text) dt.preferences.write(MODULE, "unknown_tag", "string", fc.unknown_tag.text)
dt.preferences.write(MODULE, "no_persons_found_tag", "string", fc.no_persons_found_tag.text)
dt.preferences.write(MODULE, "ignore_tags", "string", fc.ignore_tags.text) dt.preferences.write(MODULE, "ignore_tags", "string", fc.ignore_tags.text)
dt.preferences.write(MODULE, "max_width", "integer", tonumber(fc.width.text)) dt.preferences.write(MODULE, "category_tags", "string", fc.category_tags.text)
dt.preferences.write(MODULE, "max_height", "integer", tonumber(fc.height.text)) dt.preferences.write(MODULE, "known_image_path", "directory", fc.known_image_path.value)
dt.preferences.write(MODULE, "num_cores", "integer", fc.num_cores.value)
local val = fc.tolerance.value local val = fc.tolerance.value
val = string.gsub(tostring(val), ",", ".") val = string.gsub(tostring(val), ",", ".")
dt.preferences.write(MODULE, "tolerance", "float", tonumber(val)) dt.preferences.write(MODULE, "tolerance", "float", tonumber(val))
dt.preferences.write(MODULE, "num_cores", "integer", fc.num_cores.value)
dt.preferences.write(MODULE, "export_format", "integer", fc.export_format.selected)
dt.preferences.write(MODULE, "max_width", "integer", tonumber(fc.width.text))
dt.preferences.write(MODULE, "max_height", "integer", tonumber(fc.height.text))
end
local function reset_preferences()
fc.unknown_tag.text = "unknown_person"
fc.no_persons_found_tag.text = "no_persons_found"
fc.ignore_tags.text = ""
fc.category_tags.text = ""
fc.known_image_path.value = dt.configuration.config_dir .. "/face_recognition"
fc.tolerance.value = 0.6
fc.num_cores.value = -1
fc.export_format.selected = 1
fc.width.text = 1000
fc.height.text = 1000
save_preferences()
end end
-- Check if image has ignored tag attached -- Check if image has ignored tag attached
@@ -178,7 +189,7 @@ local function ignoreByTag (image, ignoreTags)
end end
end end
end end
return ignoreImage return ignoreImage
end end
@@ -204,40 +215,47 @@ local function face_recognition ()
local knownPath = dt.preferences.read(MODULE, "known_image_path", "directory") local knownPath = dt.preferences.read(MODULE, "known_image_path", "directory")
local nrCores = dt.preferences.read(MODULE, "num_cores", "integer") local nrCores = dt.preferences.read(MODULE, "num_cores", "integer")
local ignoreTagString = dt.preferences.read(MODULE, "ignore_tags", "string") local ignoreTagString = dt.preferences.read(MODULE, "ignore_tags", "string")
local categoryTagString = dt.preferences.read(MODULE, "category_tags", "string")
local unknownTag = dt.preferences.read(MODULE, "unknown_tag", "string") local unknownTag = dt.preferences.read(MODULE, "unknown_tag", "string")
local nonpersonsfoundTag = dt.preferences.read(MODULE, "no_persons_found_tag", "string")
-- face_recognition uses -1 for all cores, we use 0 in preferences -- face_recognition uses -1 for all cores, we use 0 in preferences
if nrCores < 1 then if nrCores < 1 then
nrCores = -1 nrCores = -1
end end
-- Split ignore tags (if any) -- Split ignore tags (if any)
ignoreTags = {} ignoreTags = {}
for tag in string.gmatch(ignoreTagString, '([^,]+)') do for tag in string.gmatch(ignoreTagString, '([^,]+)') do
table.insert (ignoreTags, tag) table.insert (ignoreTags, tag)
dt.print_log ("Face recognition: Ignore tag: " .. tag) dt.print_log ("Face recognition: Ignore tag: " .. tag)
end end
-- list of exported images -- list of exported images
local image_table, cnt = build_image_table(dt.gui.action_images) local image_table, cnt = build_image_table(dt.gui.action_images)
if cnt > 0 then if cnt > 0 then
local success = do_export(image_table) local success = do_export(image_table, cnt)
if success then if success then
-- do the face recognition -- do the face recognition
local img_list = {} local img_list = {}
for img,v in pairs(image_table) do for v,_ in pairs(image_table) do
table.insert (img_list, v) table.insert (img_list, v)
end end
-- Get path of exported images -- Get path of exported images
local path = df.get_path (img_list[1]) local path = df.get_path (img_list[1])
dt.print_log ("Face recognition: Path to known faces: " .. knownPath)
dt.print_log ("Face recognition: Path to unknown images: " .. path) dt.print_log ("Face recognition: Path to unknown images: " .. path)
dt.print_log ("Face recognition: Tag used for unknown faces: " .. unknownTag)
dt.print_log ("Face recognition: Tag used if non person is found: " .. nonpersonsfoundTag)
os.setlocale("C")
local tolerance = dt.preferences.read(MODULE, "tolerance", "float") local tolerance = dt.preferences.read(MODULE, "tolerance", "float")
local command = bin_path .. " --cpus " .. nrCores .. " --tolerance " .. tolerance .. " " .. knownPath .. " " .. path .. " > " .. OUTPUT local command = bin_path .. " --cpus " .. nrCores .. " --tolerance " .. tolerance .. " " .. knownPath .. " " .. path .. " > " .. OUTPUT
os.setlocale()
dt.print_log("Face recognition: Running command: " .. command) dt.print_log("Face recognition: Running command: " .. command)
dt.print(_("Starting face recognition...")) dt.print(_("Starting face recognition..."))
@@ -245,53 +263,80 @@ local function face_recognition ()
-- Open output file -- Open output file
local f = io.open(OUTPUT, "rb") local f = io.open(OUTPUT, "rb")
if not f then if not f then
dt.print(_("Face recognition failed")) dt.print(_("Face recognition failed"))
else else
dt.print(_("Face recognition finished")) dt.print(_("Face recognition finished"))
f:close () f:close ()
end end
-- Read output -- Read output
dt.print(_("processing results...")) dt.print(_("processing results..."))
local result = {} local result = {}
for line in io.lines(OUTPUT) do local tags_list = {}
local file, tag = string.match (line, "(.*),(.*)$") local tag_object = {}
tag = string.gsub (tag, "%d*$", "") for line in io.lines(OUTPUT) do
dt.print_log ("File:"..file .." Tag:".. tag) if not string.match(line, "^WARNING:") and line ~= "" and line ~= nil then
if result[file] ~= nil then local file, tag = string.match (line, "(.*),(.*)$")
table.insert (result[file], tag) tag = string.gsub (tag, "%d*$", "")
else dt.print_log ("File:"..file .." Tag:".. tag)
result[file] = {tag} tag_object = {}
if result[file] == nil then
tag_object[tag] = true
result[file] = tag_object
else
tag_object = result[file]
tag_object[tag] = true
result[file] = tag_object
end
end end
end end
-- Attach tags -- Attach tags
local result_index = 0
for file,tags in pairs(result) do for file,tags in pairs(result) do
result_index = result_index +1
-- Find image in table -- Find image in table
for img,file2 in pairs(image_table) do img = image_table[file]
if file == file2 then if img == nil then
for _,t in ipairs (tags) do dt.print_log("Face recognition: Ignoring face recognition entry: " .. file)
-- Check if image is ignored else
if ignoreByTag (img, ignoreTags) then for t,_ in pairs (tags) do
dt.print_log("Face recognition: Ignoring image with ID " .. img.id) -- Check if image is ignored
else if ignoreByTag (img, ignoreTags) then
-- Check of unrecognized unknown_person dt.print_log("Face recognition: Ignoring image with ID " .. img.id)
if t == "unknown_person" then else
t = unknownTag -- Check of unrecognized unknown_person
end if t == "unknown_person" then
t = unknownTag
end
-- Check of unrecognized no_persons_found
if t == "no_persons_found" then
t = nonpersonsfoundTag
end
if t ~= "" and t ~= nil then
if categoryTagString ~= "" and t ~= nonpersonsfoundTag then
t = categoryTagString .. "|" .. t
end
dt.print_log ("ImgId:" .. img.id .. " Tag:".. t) dt.print_log ("ImgId:" .. img.id .. " Tag:".. t)
-- Create tag if it does not exists -- Create tag if it does not exist
local tag = dt.tags.create (t) if tags_list[t] == nil then
tag = dt.tags.create (t)
tags_list[t] = tag
else
tag = tags_list[t]
end
img:attach_tag (tag) img:attach_tag (tag)
end end
end end
end end
end end
end end
dt.print(_("face recognition complete"))
cleanup(img_list) cleanup(img_list)
dt.print_log("img_list cleaned-up")
dt.print_log("face recognition complete")
dt.print(_("face recognition complete"))
else else
dt.print(_("image export failed")) dt.print(_("image export failed"))
return return
@@ -300,8 +345,30 @@ local function face_recognition ()
dt.print(_("no images selected")) dt.print(_("no images selected"))
return return
end end
end
local function install_module()
if not fc.module_installed then
dt.register_lib(
MODULE, -- Module name
_("face recognition"), -- Visible name
true, -- expandable
true, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 300}}, -- containers
fc.widget,
nil,-- view_enter
nil -- view_leave
)
fc.module_installed = true
end
end
local function destroy()
dt.gui.libs[MODULE].visible = false
end
local function restart()
dt.gui.libs[MODULE].visible = true
end end
-- build the interface -- build the interface
@@ -312,12 +379,24 @@ fc.unknown_tag = dt.new_widget("entry"){
editable = true, editable = true,
} }
fc.no_persons_found_tag = dt.new_widget("entry"){
text = dt.preferences.read(MODULE, "no_persons_found_tag", "string"),
tooltip = _("tag to be used when no persons are found"),
editable = true,
}
fc.ignore_tags = dt.new_widget("entry"){ fc.ignore_tags = dt.new_widget("entry"){
text = dt.preferences.read(MODULE, "ignore_tags", "string"), text = dt.preferences.read(MODULE, "ignore_tags", "string"),
tooltip = _("tags of images to ignore"), tooltip = _("tags of images to ignore"),
editable = true, editable = true,
} }
fc.category_tags = dt.new_widget("entry"){
text = dt.preferences.read(MODULE, "category_tags", "string"),
tooltip = _("tag category"),
editable = true,
}
fc.tolerance = dt.new_widget("slider"){ fc.tolerance = dt.new_widget("slider"){
label = _("tolerance"), label = _("tolerance"),
tooltip = ("detection tolerance - 0.6 default - lower if too many faces detected"), tooltip = ("detection tolerance - 0.6 default - lower if too many faces detected"),
@@ -384,8 +463,12 @@ fc.execute = dt.new_widget("button"){
local widgets = { local widgets = {
dt.new_widget("label"){ label = _("unknown person tag")}, dt.new_widget("label"){ label = _("unknown person tag")},
fc.unknown_tag, fc.unknown_tag,
dt.new_widget("label"){ label = _("togs of images to ignore")}, dt.new_widget("label"){ label = _("no persons found tag")},
fc.no_persons_found_tag,
dt.new_widget("label"){ label = _("tags of images to ignore")},
fc.ignore_tags, fc.ignore_tags,
dt.new_widget("label"){ label = _("tag category")},
fc.category_tags,
dt.new_widget("label"){ label = _("face data directory")}, dt.new_widget("label"){ label = _("face data directory")},
fc.known_image_path, fc.known_image_path,
} }
@@ -411,26 +494,43 @@ table.insert(widgets, fc.execute)
fc.widget = dt.new_widget("box"){ fc.widget = dt.new_widget("box"){
orientation = vertical, orientation = vertical,
table.unpack(widgets) reset_callback = function(this)
reset_preferences()
end,
table.unpack(widgets),
} }
--fc.tolerance.value = dt.preferences.read(MODULE, "tolerance", "float") if dt.gui.current_view().id == "lighttable" then
install_module()
-- Register else
--dt.register_storage("module_face_recognition", _("Face recognition"), show_status, face_recognition) if not fc.event_registered then
dt.register_event(
dt.register_lib( MODULE, "view-changed",
"face_recognition", -- Module name function(event, old_view, new_view)
_("face recognition"), -- Visible name if new_view.name == "lighttable" and old_view.name == "darkroom" then
true, -- expandable install_module()
false, -- resetable end
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 300}}, -- containers end
fc.widget, )
nil,-- view_enter fc.event_registered = true
nil -- view_leave end
) end
fc.tolerance.value = dt.preferences.read(MODULE, "tolerance", "float") fc.tolerance.value = dt.preferences.read(MODULE, "tolerance", "float")
-- preferences
if not dt.preferences.read(MODULE, "initialized", "bool") then
reset_preferences()
save_preferences()
dt.preferences.write(MODULE, "initialized", "bool", true)
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data
-- --
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

View File

@@ -0,0 +1,132 @@
--[[ fujifilm_dynamic_range-0.1
Compensate for Fujifilm raw files made using "dynamic range".
Copyright (C) 2021, 2022 Dan Torop <dant@pnym.net>
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.
]]
--[[About this Plugin
Support for adjusting darktable exposure by Fujifilm raw exposure
bias. This corrects for a DR100/DR200/DR400 "dynamic range" setting.
Dependencies:
- exiftool (https://www.sno.phy.queensu.ca/~phil/exiftool/)
Based upon fujifilm_ratings by Ben Mendis
The relevant tag is RawExposureBias (0x9650). This appears to
represent the shift in EV for the chosen DR setting (whether manual or
automatic). Note that even at 100DR ("standard") there is an EV shift:
100 DR -> -0.72 EV
200 DR -> -1.72 EV
400 DR -> -2.72 EV
The ideal would be to use exiv2 to read this tag, as this is the same
code which darktable import uses. Unfortunately, exiv2 as of v0.27.3
can't read this tag. As it is encoded as a 4-byte ratio of two signed
shorts -- a novel data type -- it will require some attention to fix
this.
There is an exiv2-readable DevelopmentDynamicRange tag which maps to
RawExposureBias as above. DevelopmentDynamicRange is only present
when tag DynamicRangeSetting (0x1402) is Manual/Raw (0x0001). When it
is Auto (0x0000), the equivalent data is tag AutoDynamicRange
(0x140b). But exiv2 currently can't read that tag either.
Hence for now this code uses exiftool to read RawExposureBias, as a
more general solution. As exiftool is approx. 10x slower than exiv2
(Perl vs. C++), this may slow large imports.
These tags have been checked on a Fujifilm X100S and X100V. Other
cameras may behave in other ways.
--]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
du.check_min_api_version("7.0.0", "fujifilm_dynamic_range")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local function detect_dynamic_range(event, image)
if image.exif_maker ~= "FUJIFILM" then
dt.print_log("[fujifilm_dynamic_range] ignoring non-Fujifilm image")
return
end
-- it would be nice to check image.is_raw but this appears to not yet be set
if not string.match(image.filename, "%.RAF$") then
dt.print_log("[fujifilm_dynamic_range] ignoring non-raw image")
return
end
local command = df.check_if_bin_exists("exiftool")
if not command then
dt.print_error("[fujifilm_dynamic_range] exiftool not found")
return
end
local RAF_filename = df.sanitize_filename(tostring(image))
-- without -n flag, exiftool will round to the nearest tenth
command = command .. " -RawExposureBias -n -t " .. RAF_filename
dt.print_log(command)
output = io.popen(command)
local raf_result = output:read("*all")
output:close()
if #raf_result == 0 then
dt.print_error("[fujifilm_dynamic_range] no output returned by exiftool")
return
end
raf_result = string.match(raf_result, "\t(.*)")
if not raf_result then
dt.print_error("[fujifilm_dynamic_range] could not parse exiftool output")
return
end
if image.exif_exposure_bias ~= image.exif_exposure_bias then
-- is NAN (this is unlikely as RAFs should have ExposureBiasValue set)
image.exif_exposure_bias = 0
end
-- this should be auto-applied if plugins/darkroom/workflow is scene-referred
-- note that scene-referred workflow exposure preset also pushes exposure up by 0.5 EV
image.exif_exposure_bias = image.exif_exposure_bias + tonumber(raf_result)
dt.print_log("[fujifilm_dynamic_range] raw exposure bias " .. tostring(raf_result))
end
local function destroy()
dt.destroy_event("fujifilm_dr", "post-import-image")
end
if not df.check_if_bin_exists("exiftool") then
dt.print_log("Please install exiftool to use fujifilm_dynamic_range")
error "[fujifilm_dynamic_range] exiftool not found"
end
dt.register_event("fujifilm_dr", "post-import-image",
detect_dynamic_range)
dt.print_log("[fujifilm_dynamic_range] loaded")
script_data.destroy = destroy
return script_data

View File

@@ -28,7 +28,16 @@ local du = require "lib/dtutils"
local df = require "lib/dtutils.file" local df = require "lib/dtutils.file"
local gettext = dt.gettext local gettext = dt.gettext
du.check_min_api_version("4.0.0", "fujifilm_ratings") du.check_min_api_version("7.0.0", "fujifilm_ratings")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
gettext.bindtextdomain("fujifilm_ratings", dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("fujifilm_ratings", dt.configuration.config_dir.."/lua/locale/")
@@ -41,7 +50,7 @@ local function detect_rating(event, image)
dt.print_error(_("exiftool not found")) dt.print_error(_("exiftool not found"))
return return
end end
local RAF_filename = tostring(image) local RAF_filename = df.sanitize_filename(tostring(image))
local JPEG_filename = string.gsub(RAF_filename, "%.RAF$", ".JPG") local JPEG_filename = string.gsub(RAF_filename, "%.RAF$", ".JPG")
local command = "exiftool -Rating " .. JPEG_filename local command = "exiftool -Rating " .. JPEG_filename
dt.print_error(command) dt.print_error(command)
@@ -66,6 +75,12 @@ local function detect_rating(event, image)
end end
end end
dt.register_event("post-import-image", detect_rating) local function destroy()
dt.destroy_event("fujifilm_rat", "post-import-image")
end
print(_("fujifilm_ratings loaded.")) dt.register_event("fujifilm_rat", "post-import-image",
detect_rating)
script_data.destroy = destroy
return script_data

View File

@@ -35,10 +35,18 @@ USAGE
local dt = require "darktable" local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
local df = require "lib/dtutils.file" local df = require "lib/dtutils.file"
require "official/yield"
local gettext = dt.gettext local gettext = dt.gettext
du.check_min_api_version("3.0.0", "geoJSON_export") du.check_min_api_version("7.0.0", "geoJSON_export")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("geoJSON_export",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("geoJSON_export",dt.configuration.config_dir.."/lua/locale/")
@@ -292,6 +300,10 @@ local function create_geoJSON_file(storage, image_table, extra_data)
end end
local function destroy()
dt.destroy_storage("geoJSON_export")
end
-- Preferences -- Preferences
dt.preferences.register("geoJSON_export", dt.preferences.register("geoJSON_export",
"CreateMapBoxHTMLFile", "CreateMapBoxHTMLFile",
@@ -327,3 +339,7 @@ dt.preferences.register("geoJSON_export",
-- Register -- Register
dt.register_storage("geoJSON_export", "geoJSON Export", nil, create_geoJSON_file) dt.register_storage("geoJSON_export", "geoJSON Export", nil, create_geoJSON_file)
script_data.destroy = destroy
return script_data

View File

@@ -28,10 +28,18 @@ require "geoToolbox"
local dt = require "darktable" local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
local df = require "lib/dtutils.file" local df = require "lib/dtutils.file"
require "official/yield"
local gettext = dt.gettext local gettext = dt.gettext
du.check_min_api_version("3.0.0", "geoToolbox") du.check_min_api_version("7.0.0", "geoToolbox")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("geoToolbox",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("geoToolbox",dt.configuration.config_dir.."/lua/locale/")
@@ -40,6 +48,12 @@ local function _(msgid)
return gettext.dgettext("geoToolbox", msgid) return gettext.dgettext("geoToolbox", msgid)
end end
local gT = {}
gT.module_installed = false
gT.event_registered = false
-- <GUI> -- <GUI>
local labelDistance = dt.new_widget("label") local labelDistance = dt.new_widget("label")
labelDistance.label = _("Distance:") labelDistance.label = _("Distance:")
@@ -417,7 +431,7 @@ local function get_distance(lat1, lon1, ele1, lat2, lon2, ele2)
math.cos(math.rad(lat1)) * math.cos(math.rad(lat2)) * math.cos(math.rad(lat1)) * math.cos(math.rad(lat2)) *
math.sin(dLon/2) * math.sin(dLon/2) math.sin(dLon/2) * math.sin(dLon/2)
; ;
local angle = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)); local angle = 2 * math.atan(math.sqrt(a), math.sqrt(1-a));
local distance = earthRadius * angle; -- Distance in km local distance = earthRadius * angle; -- Distance in km
-- Add the elevation to the calculation -- Add the elevation to the calculation
@@ -572,6 +586,49 @@ local function altitude_profile()
end end
local function install_module()
if not gT.module_installed then
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
gT.widget,
nil,-- view_enter
nil -- view_leave
)
gT.module_installed = true
end
end
local function destroy()
dt.gui.libs["geoToolbox"].visible = false
dt.destroy_event("geoToolbox_cd", "shortcut")
dt.destroy_event("geoToolbox", "mouse-over-image-changed")
dt.destroy_event("geoToolbox_wg", "shortcut")
dt.destroy_event("geoToolbox_ng", "shortcut")
end
local function restart()
dt.register_event("geoToolbox_cd", "shortcut",
print_calc_distance, _("Calculate the distance from latitude and longitude in km"))
dt.register_event("geoToolbox", "mouse-over-image-changed",
toolbox_calc_distance)
dt.register_event("geoToolbox_wg", "shortcut",
select_with_gps, _("Select all images with GPS information"))
dt.register_event("geoToolbox_ng", "shortcut",
select_without_gps, _("Select all images without GPS information"))
dt.gui.libs["geoToolbox"].visible = true
end
local function show()
dt.gui.libs["geoToolbox"].visible = true
end
local separator = dt.new_widget("separator"){} local separator = dt.new_widget("separator"){}
local separator2 = dt.new_widget("separator"){} local separator2 = dt.new_widget("separator"){}
@@ -579,13 +636,7 @@ local separator3 = dt.new_widget("separator"){}
local separator4 = dt.new_widget("separator"){} local separator4 = dt.new_widget("separator"){}
local separator5 = dt.new_widget("separator"){} local separator5 = dt.new_widget("separator"){}
dt.register_lib( gT.widget = dt.new_widget("box")
"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", orientation = "vertical",
dt.new_widget("button") dt.new_widget("button")
@@ -664,10 +715,24 @@ dt.register_lib(
clicked_callback = altitude_profile clicked_callback = altitude_profile
}, },
labelDistance labelDistance
}, }
nil,-- view_enter
nil -- view_leave
) if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not gT.event_registered then
dt.register_event(
"geoToolbox", "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
gT.event_registered = true
end
end
-- Preferences -- Preferences
dt.preferences.register("geoToolbox", dt.preferences.register("geoToolbox",
@@ -678,11 +743,21 @@ dt.preferences.register("geoToolbox",
'' ) '' )
-- Register -- Register
dt.register_event("shortcut", print_calc_distance, _("Calculate the distance from latitude and longitude in km")) dt.register_event("geoToolbox_cd", "shortcut",
dt.register_event("mouse-over-image-changed", toolbox_calc_distance) print_calc_distance, _("Calculate the distance from latitude and longitude in km"))
dt.register_event("geoToolbox", "mouse-over-image-changed",
toolbox_calc_distance)
dt.register_event("shortcut", select_with_gps, _("Select all images with GPS information")) dt.register_event("geoToolbox_wg", "shortcut",
dt.register_event("shortcut", select_without_gps, _("Select all images without GPS information")) select_with_gps, _("Select all images with GPS information"))
dt.register_event("geoToolbox_ng", "shortcut",
select_without_gps, _("Select all images without GPS information"))
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = show
return script_data
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua; -- kate: hl Lua;

View File

@@ -41,7 +41,9 @@
* require this script from your main lua file * require this script from your main lua file
* select an image or images for editing with GIMP * 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 * in the export dialog select "Edit with GIMP" and select the format and bit depth for the
exported image exported image. Check the "run_detached" button to run GIMP in detached mode. Images
will not be returned to darktable in this mode, but additional images can be sent to
GIMP without stopping it.
* Press "export" * Press "export"
* Edit the image with GIMP then save the changes with File->Overwrite.... * Edit the image with GIMP then save the changes with File->Overwrite....
* Exit GIMP * Exit GIMP
@@ -70,7 +72,16 @@ local dtsys = require "lib/dtutils.system"
local gettext = dt.gettext local gettext = dt.gettext
local gimp_widget = nil local gimp_widget = nil
du.check_min_api_version("5.0.0", "gimp") du.check_min_api_version("7.0.0", "gimp")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("gimp",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("gimp",dt.configuration.config_dir.."/lua/locale/")
@@ -103,6 +114,8 @@ end
local function gimp_edit(storage, image_table, extra_data) --finalize local function gimp_edit(storage, image_table, extra_data) --finalize
local run_detached = dt.preferences.read("gimp", "run_detached", "bool")
local gimp_executable = df.check_if_bin_exists("gimp") local gimp_executable = df.check_if_bin_exists("gimp")
if not gimp_executable then if not gimp_executable then
@@ -111,7 +124,11 @@ local function gimp_edit(storage, image_table, extra_data) --finalize
end end
if dt.configuration.running_os == "macos" then if dt.configuration.running_os == "macos" then
gimp_executable = "open -W -a " .. gimp_executable if run_detached then
gimp_executable = "open -a " .. gimp_executable
else
gimp_executable = "open -W -a " .. gimp_executable
end
end end
-- list of exported images -- list of exported images
@@ -130,55 +147,75 @@ local function gimp_edit(storage, image_table, extra_data) --finalize
local gimpStartCommand local gimpStartCommand
gimpStartCommand = gimp_executable .. " " .. img_list gimpStartCommand = gimp_executable .. " " .. img_list
if run_detached then
if dt.configuration.running_os == "windows" then
gimpStartCommand = "start /b \"\" " .. gimpStartCommand
else
gimpStartCommand = gimpStartCommand .. " &"
end
end
dt.print_log(gimpStartCommand) dt.print_log(gimpStartCommand)
dtsys.external_command(gimpStartCommand) dtsys.external_command(gimpStartCommand)
-- for each of the image, exported image pairs if not run_detached then
-- 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 -- 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
local myimage_name = image.path .. "/" .. df.get_filename(exported_image) for image,exported_image in pairs(image_table) do
while df.check_if_file_exists(myimage_name) do local myimage_name = image.path .. "/" .. df.get_filename(exported_image)
myimage_name = df.filename_increment(myimage_name)
-- limit to 99 more exports of the original export while df.check_if_file_exists(myimage_name) do
if string.match(df.get_basename(myimage_name), "_(d-)$") == "99" then myimage_name = df.filename_increment(myimage_name)
break -- limit to 99 more exports of the original export
if string.match(df.get_basename(myimage_name), "_(d-)$") == "99" then
break
end
end end
end
dt.print_log("moving " .. exported_image .. " to " .. myimage_name) dt.print_log("moving " .. exported_image .. " to " .. myimage_name)
local result = df.file_move(exported_image, myimage_name) local result = df.file_move(exported_image, myimage_name)
if result then if result then
dt.print_log("importing file") dt.print_log("importing file")
local myimage = dt.database.import(myimage_name) local myimage = dt.database.import(myimage_name)
group_if_not_member(image, myimage) group_if_not_member(image, myimage)
for _,tag in pairs(dt.tags.get_tags(image)) do for _,tag in pairs(dt.tags.get_tags(image)) do
if not (string.sub(tag.name,1,9) == "darktable") then if not (string.sub(tag.name,1,9) == "darktable") then
dt.print_log("attaching tag") dt.print_log("attaching tag")
dt.tags.attach(tag,myimage) dt.tags.attach(tag,myimage)
end
end end
end end
end end
end end
end
local function destroy()
dt.destroy_storage("module_gimp")
end end
-- Register -- Register
local executables = {"gimp"} gimp_widget = dt.new_widget("check_button"){
label = _("run detached"),
if dt.configuration.running_os ~= "linux" then tooltip = _("don't import resulting image back into darktable"),
gimp_widget = df.executable_path_widget(executables) value = dt.preferences.read("gimp", "run_detached", "bool"),
end clicked_callback = function(this)
dt.preferences.write("gimp", "run_detached", "bool", this.value)
end
}
dt.register_storage("module_gimp", _("Edit with GIMP"), show_status, gimp_edit, nil, nil, gimp_widget) dt.register_storage("module_gimp", _("Edit with GIMP"), show_status, gimp_edit, nil, nil, gimp_widget)
-- --
script_data.destroy = destroy
return script_data

View File

@@ -27,7 +27,16 @@ local df = require "lib/dtutils.file"
local dl = require "lib/dtutils" local dl = require "lib/dtutils"
local gettext = dt.gettext local gettext = dt.gettext
dl.check_min_api_version("3.0.0", "gpx-export") dl.check_min_api_version("7.0.0", "gpx_export")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("gpx_export",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("gpx_export",dt.configuration.config_dir.."/lua/locale/")
@@ -36,6 +45,10 @@ local function _(msgid)
return gettext.dgettext("gpx_export", msgid) return gettext.dgettext("gpx_export", msgid)
end end
local gpx = {}
gpx.module_installed = false
gpx.event_registered = false
local path_entry = dt.new_widget("entry") local path_entry = dt.new_widget("entry")
{ {
text = dt.preferences.read("gpx_exporter", "gpxExportPath", "string"), text = dt.preferences.read("gpx_exporter", "gpxExportPath", "string"),
@@ -127,31 +140,70 @@ local function create_gpx_file()
end end
end end
dt.register_lib( local function install_module()
"gpx_exporter", if not gpx.module_installed then
"gpx export", dt.register_lib(
true, -- expandable "gpx_exporter",
true, -- resetable "gpx export",
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers true, -- expandable
true, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
gpx.widget,
nil,-- view_enter
nil -- view_leave
)
gpx.module_installed = true
end
end
local function destroy()
dt.gui.libs["gpx_exporter"].visible = false
end
local function restart()
dt.gui.libs["gpx_exporter"].visible = true
end
gpx.widget = dt.new_widget("box")
{
orientation = "vertical",
dt.new_widget("button")
{
label = _("export"),
tooltip = _("export gpx file"),
clicked_callback = create_gpx_file
},
dt.new_widget("box") dt.new_widget("box")
{ {
orientation = "vertical", orientation = "horizontal",
dt.new_widget("button") dt.new_widget("label")
{ {
label = _("export"), label = _("file:"),
tooltip = _("export gpx file"),
clicked_callback = create_gpx_file
},
dt.new_widget("box")
{
orientation = "horizontal",
dt.new_widget("label")
{
label = _("file:"),
},
path_entry
}, },
path_entry
}, },
nil,-- view_enter }
nil -- view_leave
)
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not gpx.event_registered then
dt.register_event(
"gpx_export", "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
gpx.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data

View File

@@ -0,0 +1,78 @@
--[[
harmonic artmature guide for darktable
copyright (c) 2021 Hubert Kowalski
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/>.
]]
--[[
HARMONIC ARMATURE GUIDE
Harmonic Armature (also known as 14 line armature)
INSTALLATION
* copy this file in $CONFIGDIR/lua/contrib where CONFIGDIR is your darktable configuration directory
* add the following line in the file $CONFIGDIR/luarc
require "contrib/harmonic_armature_guide"
USAGE
* when using guides, select "harmonic armature" as guide
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local gettext = dt.gettext
du.check_min_api_version("2.0.0", "harmonic_armature_guide")
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("harmonic_armature_guide",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("harmonic_armature_guide", msgid)
end
dt.guides.register_guide("harmonic armature",
-- draw
function(cairo, x, y, width, height, zoom_scale)
cairo:save()
cairo:translate(x, y)
cairo:scale(width, height)
cairo:move_to(0,0)
cairo:line_to(1, 0.5)
cairo:line_to(0.5, 1)
cairo:line_to(0,0)
cairo:line_to(1, 1)
cairo:line_to(0.5, 0)
cairo:line_to(0, 0.5)
cairo:line_to(1, 1)
cairo:move_to(1, 0)
cairo:line_to(0, 0.5)
cairo:line_to(0.5, 1)
cairo:line_to(1, 0)
cairo:line_to(0, 1)
cairo:line_to(0.5, 0)
cairo:line_to(1, 0.5)
cairo:line_to(0, 1)
cairo:restore()
end,
-- gui
function()
return dt.new_widget("label"){label = _("harmonic armature"), halign = "start"}
end
)

View File

@@ -40,7 +40,6 @@ local du = require "lib/dtutils"
local df = require "lib/dtutils.file" local df = require "lib/dtutils.file"
local log = require "lib/dtutils.log" local log = require "lib/dtutils.log"
local dtsys = require "lib/dtutils.system" local dtsys = require "lib/dtutils.system"
require "official/yield"
local gettext = dt.gettext local gettext = dt.gettext
local namespace = 'module_hugin' local namespace = 'module_hugin'
@@ -55,7 +54,16 @@ local executable_table = {"hugin", "hugin_executor", "pto_gen"}
local PQ = dt.configuration.running_os == "windows" and '"' or "'" local PQ = dt.configuration.running_os == "windows" and '"' or "'"
-- works with darktable API version from 5.0.0 on -- works with darktable API version from 5.0.0 on
du.check_min_api_version("5.0.0", "hugin") du.check_min_api_version("7.0.0", "hugin")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("hugin",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("hugin",dt.configuration.config_dir.."/lua/locale/")
@@ -206,6 +214,10 @@ local function create_panorama(storage, image_table, extra_data) --finalize
end end
end end
local function destroy()
dt.destroy_storage(namespace)
end
-- Register -- Register
if dt.configuration.running_os ~= "linux" then if dt.configuration.running_os ~= "linux" then
exec_widget = df.executable_path_widget(executable_table) exec_widget = df.executable_path_widget(executable_table)
@@ -225,6 +237,8 @@ hugin_widget = dt.new_widget("box") {
dt.register_storage(namespace, _("hugin panorama"), show_status, create_panorama, nil, nil, hugin_widget) dt.register_storage(namespace, _("hugin panorama"), show_status, create_panorama, nil, nil, hugin_widget)
script_data.destroy = destroy
return script_data
-- --
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

View File

@@ -71,7 +71,16 @@ local job = nil
local PS = dt.configuration.running_os == "windows" and "\\" or "/" local PS = dt.configuration.running_os == "windows" and "\\" or "/"
-- works with LUA API version 5.0.0 -- works with LUA API version 5.0.0
du.check_min_api_version("5.0.0", "image_stack") du.check_min_api_version("7.0.0", "image_stack")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("image_stack",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("image_stack",dt.configuration.config_dir.."/lua/locale/")
@@ -452,6 +461,10 @@ local function stop_job()
job.valid = false job.valid = false
end end
local function destroy()
dt.destroy_storage("module_image_stack")
end
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- main program -- main program
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -585,3 +598,6 @@ dt.preferences.register("align_image_stack", "align_use_gpu", -- name
dt.register_storage("module_image_stack", _("image stack"), show_status, image_stack, nil, nil, image_stack_widget) dt.register_storage("module_image_stack", _("image stack"), show_status, image_stack, nil, nil, image_stack_widget)
script_data.destroy = destroy
return script_data

587
contrib/image_time.lua Normal file
View File

@@ -0,0 +1,587 @@
--[[
image_time.lua - synchronize image time for images shot with different cameras
Copyright (C) 2019, 2020 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/>.
]]
--[[
image_time - non-destructively modify the image time
DESCRIPTION
image_time non destructively adjusts image times by modifying the
database image exif_datetime_taken field. There are 4 modes: adjust time,
set time, synchronize time, and reset time.
ADJUST TIME
adjust time mode lets you chose an offset in terms of years, months,
days, hours, minutes, and seconds. The adjustment can be added or
subtracted.
WARNING: When adding and subtracting months the result will usually
be what is expected unless the time being adjusted is at the end of
the month. This is because a month is a variable amount of time that
can be 28, 29, 30 or 31 days depending on the month. Example: It's
March 31st and I subtract a month which not sets the time to February
31st. When that gets set to a valid time, then the date changes to
March 3rd.
SET TIME
set time mode allows you to pick a date and time and set the image
time accordingly. Fields may be left out. This is useful when
importing scanned images that don't have an embedded date.
SYNCHRONIZE TIME
I recently purchased a 7DmkII to replace my aging 7D. My 7D was still
serviceable, so I bought a remote control and figured I'd try shooting
events from 2 different perspectives. I didn't think to synchonize the
time between the 2 cameras, so when I loaded the images and sorted by
time it was a disaster. I hacked a script together with hard coded values
to adjust the exif_datetime_taken value in the database for the 7D images
so that everything sorted properly. I've tried shooting with 2 cameras
several times since that first attempt. I've gotten better at getting the
camera times close, but still haven't managed to get them to sync. So I
decided to think the problem through and write a proper script to take
care of the problem.
RESET TIME
Select the images and click reset.
USAGE
ADJUST TIME
Change the year, month, day, hour, minute, second dropdowns to the amount
of change desired. Select add or subtract. Select the images. Click
adjust.
SET TIME
Set the time fields to the desired time. Select the images to change. Click
set.
SYNCHRONIZE TIME
Select 2 images, one from each camera, of the same moment in time. Click
the Calculate button to calculate the time difference. The difference is
displayed in the difference entry. You can manually adjust it by changing
the value if necessary.
Select the images that need their time adjusted. Determine which way to adjust
adjust the time (add or subtract) and select the appropriate choice.
If the image times get messed up and you just want to start over, select reset time
from the mode and reset the image times.
RESET TIME
Select the images and click reset.
ADDITIONAL SOFTWARE REQUIRED
* exiv2
BUGS, COMMENTS, SUGGESTIONS
* Send to Bill Ferguson, wpferguson@gmail.com
CHANGES
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local ds = require "lib/dtutils.string"
local gettext = dt.gettext
local img_time = {}
img_time.module_installed = false
img_time.event_registered = false
du.check_min_api_version("7.0.0", "image_time")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("image_time",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("image_time", msgid)
end
local PS = dt.configuration.runnin_os == "windows" and "\\" or "/"
local ERROR = -1
-- function to convert from exif time to system time
local function exiftime2systime(exiftime)
local yr,mo,dy,h,m,s = string.match(exiftime, "(%d-):(%d-):(%d-) (%d-):(%d-):(%d+)")
return(os.time{year=yr, month=mo, day=dy, hour=h, min=m, sec=s})
end
-- function to convert from systime to exif time
local function systime2exiftime(systime)
local t = os.date("*t", systime)
return(string.format("%4d:%02d:%02d %02d:%02d:%02d", t.year, t.month, t.day, t.hour, t.min, t.sec))
end
local function vars2exiftime(year, month, day, hour, min, sec)
local y = tonumber(year) and string.format("%4d", year) or " "
local mo = tonumber(month) and string.format("%02d", month) or " "
local d = tonumber(day) and string.format("%02d", day) or " "
local h = tonumber(hour) and string.format("%02d", hour) or " "
local m = tonumber(min) and string.format("%02d", min) or " "
local s = tonumber(sec) and string.format("%02d", sec) or " "
return(y .. ":" .. mo .. ":" .. d .. " " .. h .. ":" .. m .. ":" .. s)
end
local function exiftime2vars(exiftime)
return string.match(exiftime, "(%d+):(%d+):(%d+) (%d+):(%d+):(%d+)")
end
local function calc_time_difference(image1, image2)
return math.abs(exiftime2systime(image1.exif_datetime_taken) - exiftime2systime(image2.exif_datetime_taken))
end
local function adjust_image_time(image, difference)
image.exif_datetime_taken = systime2exiftime(exiftime2systime(image.exif_datetime_taken) + difference)
return
end
local function calculate_difference(images)
if #images == 2 then
img_time.diff_entry.text = calc_time_difference(images[1], images[2])
img_time.btn.sensitive = true
else
dt.print(_("Error: 2 images must be selected"))
end
end
local function synchronize_times(images, difference)
for _, image in ipairs(images) do
adjust_image_time(image, difference)
end
end
local function synchronize_time(images)
local sign = 1
if img_time.sdir.value == "subtract" then
sign = -1
end
synchronize_times(images, tonumber(img_time.diff_entry.text) * sign)
end
local function add_time(images)
synchronize_times(images, tonumber(img_time.diff_entry.text))
end
local function year_month2months(year, month)
year_months = tonumber(year) and year * 12 or 0
local months = tonumber(month) and tonumber(month) or 0
return year_months + months
end
local function months2year_month(months)
dt.print_log("months is " .. months)
local year = math.floor(months / 12)
local month = months - (year * 12)
return year, month
end
local function get_image_taken_time(image)
-- get original image time
local datetime = nil
local exiv2 = df.check_if_bin_exists("exiv2")
if exiv2 then
p = io.popen(exiv2 .. " -K Exif.Image.DateTime " .. image.path .. PS .. image.filename)
if p then
for line in p:lines() do
if string.match(line, "Exif.Image.DateTime") then
datetime = string.match(line, "(%d-:%d-:%d- %d-:%d-:%d+)")
end
end
p:close()
end
else
dt.print(_("unable to detect exiv2"))
datetime = ERROR
end
return datetime
end
local function _get_windows_image_file_creation_time(image)
local datetime = nil
local p = io.popen("dir " .. image.path .. PS .. image.filename)
if p then
for line in p:lines() do
if string.match(line, ds.sanitize_lua(image.filename)) then
local mo, day, yr, hr, min, apm = string.match(line, "(%d+)/(%d+)/(%d+) (%d-):(%d+) (%S+)")
if apm == "PM" then
hr = hr + 12
end
datetime = vars2exiftime(yr, mo, day, hr, min, 0)
end
end
p:close()
else
dt.print(_("unable to get information for ") .. image.filename)
datetime = ERROR
end
return datetime
end
local function _get_nix_image_file_creation_time(image)
local datetime = nil
local p = io.popen("ls -lL --time-style=full-iso " .. image.path .. PS .. image.filename)
if p then
for line in p:lines() do
if string.match(line, ds.sanitize_lua(image.filename)) then
datetime = vars2exiftime(string.match(line, "(%d+)%-(%d-)%-(%d-) (%d-):(%d-):(%d+)."))
end
end
p:close()
else
dt.print(_("unable to get information for ") .. image.filename)
datetime = ERROR
end
return datetime
end
local function get_image_file_creation_time(image)
-- no exif time in the image file so get the creation time
local datetime = nil
if dt.configuration.running_os == "windows" then
datetime = _get_windows_image_file_creation_time(image)
else
datetime = _get_nix_image_file_creation_time(image)
end
return datetime
end
local function get_original_image_time(image)
local image_time = image.exif_datetime_taken
local reset_time = nil
reset_time = get_image_taken_time(image)
if reset_time then
if reset_time == ERROR then
return image_time
else
return reset_time
end
else
reset_time = get_image_file_creation_time(image)
if reset_time then
if reset_time == ERROR then
return image_time
else
return reset_time
end
end
end
end
local function reset_time(images)
if #images > 0 then
for _, image in ipairs(images) do
image.exif_datetime_taken = get_original_image_time(image)
end
else
dt.print_error(_("reset time: no images selected"))
dt.print(_("please select the images that need their time reset"))
end
end
local function adjust_time(images)
local SEC_MIN = 60
local SEC_HR = SEC_MIN * 60
local SEC_DY = SEC_HR * 24
local offset = nil
local sign = 1
if #images < 1 then
dt.print(_("please select some images and try again"))
return
end
if img_time.adir.value == _("subtract") then
sign = -1
end
for _, image in ipairs(images) do
local y, mo, d, h, m, s = exiftime2vars(image.exif_datetime_taken)
local image_months = year_month2months(y, mo)
local months_diff = year_month2months(img_time.ayr.value, img_time.amo.value)
y, mo = months2year_month(image_months + (months_diff * sign))
local exif_new = vars2exiftime(y, mo, d, h, m, s)
offset = img_time.ady.value * SEC_DY
offset = offset + img_time.ahr.value * SEC_HR
offset = offset + img_time.amn.value * SEC_MIN
offset = offset + img_time.asc.value
offset = offset * sign
image.exif_datetime_taken = systime2exiftime(exiftime2systime(exif_new) + offset)
end
end
local function set_time(images)
if #images < 1 then
dt.print(_("please select some images and try again"))
return
end
local y = img_time.syr.value
local mo = img_time.smo.value
local d = img_time.sdy.value
local h = img_time.shr.value
local m = img_time.smn.value
local s = img_time.ssc.value
for _, image in ipairs(images) do
image.exif_datetime_taken = vars2exiftime(y, mo, d, h, m, s)
end
end
local function seq(first, last)
local result = {}
local num = first
while num <= last do
table.insert(result, num)
num = num + 1
end
return table.unpack(result)
end
local function reset_widgets()
dt.print_log("took the reset function")
img_time.ayr.selected = 1
img_time.amo.selected = 1
img_time.ady.selected = 1
img_time.ahr.selected = 1
img_time.ayr.selected = 1
img_time.amn.selected = 1
img_time.asc.selected = 1
img_time.adir.selected = 1
img_time.syr.selected = #img_time.syr
img_time.smo.selected = 1
img_time.sdy.selected = 1
img_time.shr.selected = 1
img_time.smn.selected = 1
img_time.ssc.selected = 1
img_time.adir.selected = 1
end
local function install_module()
if not img_time.module_installed then
dt.register_lib(
"image_time", -- Module name
_("image time"), -- Visible name
true, -- expandable
true, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
img_time.widget,
nil,-- view_enter
nil -- view_leave
)
img_time.module_installed = true
end
end
local function destroy()
dt.gui.libs["image_time"].visible = false
end
local function restart()
dt.gui.libs["image_time"].visible = true
end
-- widgets
img_time.widgets = {
-- name, type, tooltip, placeholder,
{"ayr", "combobox", _("years"), _("years to adjust by, 0 - ?"), {seq(0,20)}, 1},
{"amo", "combobox", _("months"), ("months to adjust by, 0-12"), {seq(0,12)}, 1},
{"ady", "combobox", _("days"), ("days to adjust by, 0-31"), {seq(0,31)}, 1},
{"ahr", "combobox", _("hours"), _("hours to adjust by, 0-23"), {seq(0,23)}, 1},
{"amn", "combobox", _("minutes"), _("minutes to adjust by, 0-59"), {seq(0,59)}, 1},
{"asc", "combobox", _("seconds"), _("seconds to adjust by, 0-59"), {seq(0,59)}, 1},
{"adir", "combobox", _("add/subtract"), _("add or subtract time"), {_("add"), _("subtract")}, 1},
{"syr", "combobox", _("year"), _("year to set, 1900 - now"), {" ", seq(1900,os.date("*t", os.time()).year)}, 1},
{"smo", "combobox", _("month"), _("month to set, 1-12"), {" ", seq(1,12)}, 1},
{"sdy", "combobox", _("day"), _("day to set, 1-31"), {" ", seq(1,31)}, 1},
{"shr", "combobox", _("hour"), _("hour to set, 0-23"), {" ", seq(0,23)}, 1},
{"smn", "combobox", _("minute"), _("minutes to set, 0-59"), {" ", seq(0, 59)}, 1},
{"ssc", "combobox", _("seconds"), _("seconds to set, 0-59"), {" ", seq(0,59)}, 1},
{"sdir", "combobox", _("add/subtract"), _("add or subtract time"), {_("add"), _("subtract")}, 1},
}
for _, widget in ipairs(img_time.widgets) do
img_time[widget[1]] = dt.new_widget(widget[2]){
label = widget[3],
tooltip = widget[4],
selected = widget[6],
table.unpack(widget[5])
}
end
img_time.syr.selected = #img_time.syr
img_time.diff_entry = dt.new_widget("entry"){
tooltip = _("Time difference between images in seconds"),
placeholder = _("Select 2 images and use the calculate button"),
text = "",
}
img_time.calc_btn = dt.new_widget("button"){
label = _("Calculate"),
tooltip = _("calculate time difference between 2 images"),
clicked_callback = function()
calculate_difference(dt.gui.action_images)
end
}
img_time.btn = dt.new_widget("button"){
label = _("synchronize image times"),
tooltip = _("apply the time difference from selected images"),
sensitive = false,
clicked_callback = function()
synchronize_time(dt.gui.action_images)
end
}
img_time.stack = dt.new_widget("stack"){
dt.new_widget("box"){
orientation = "vertical",
dt.new_widget("label"){label = _("adjust time")},
dt.new_widget("section_label"){label = _("days, months, years")},
img_time.ady,
img_time.amo,
img_time.ayr,
dt.new_widget("section_label"){label = _("hours, minutes, seconds")},
img_time.ahr,
img_time.amn,
img_time.asc,
dt.new_widget("section_label"){label = _("adjustment direction")},
img_time.adir,
dt.new_widget("button"){
label = _("adjust"),
clicked_callback = function()
adjust_time(dt.gui.action_images)
end
}
},
dt.new_widget("box"){
orientation = "vertical",
dt.new_widget("label"){label = _("set time")},
dt.new_widget("section_label"){label = _("date: ")},
img_time.sdy,
img_time.smo,
img_time.syr,
dt.new_widget("section_label"){label = _("time:")},
img_time.shr,
img_time.smn,
img_time.ssc,
dt.new_widget("button"){
label = _("set"),
clicked_callback = function()
set_time(dt.gui.action_images)
end
}
},
dt.new_widget("box"){
orientation = "vertical",
dt.new_widget("label"){label = _("synchronize image time")},
dt.new_widget("section_label"){label = _("calculate difference between images")},
img_time.diff_entry,
img_time.calc_btn,
dt.new_widget("section_label"){label = _("apply difference")},
img_time.sdir,
img_time.btn,
},
dt.new_widget("box"){
orientation = "vertical",
dt.new_widget("label"){label = _("reset to original time")},
dt.new_widget("separator"){},
dt.new_widget("button"){
label = _("reset"),
clicked_callback = function()
reset_time(dt.gui.action_images)
end
}
},
}
img_time.mode = dt.new_widget("combobox"){
label = _("mode"),
tooltip = _("select mode"),
selected = 1,
changed_callback = function(this)
img_time.stack.active = this.selected
end,
_("adjust time"),
_("set time"),
_("synchronize time"),
_("reset time")
}
img_time.widget = dt.new_widget("box"){
orientation = "vertical",
reset_callback = function(this)
reset_widgets()
end,
img_time.mode,
img_time.stack,
}
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not img_time.event_registered then
dt.register_event(
"image_time", "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
img_time.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data

View File

@@ -43,7 +43,16 @@ local gettext = dt.gettext
local PS = dt.configuration.running_os == "windows" and "\\" or "/" local PS = dt.configuration.running_os == "windows" and "\\" or "/"
du.check_min_api_version("5.0.0", kml_export) du.check_min_api_version("7.0.0", "kml_export")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("kml_export",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("kml_export",dt.configuration.config_dir.."/lua/locale/")
@@ -301,6 +310,10 @@ local function create_kml_file(storage, image_table, extra_data)
end end
local function destroy()
dt.destroy_storage("kml_export")
end
-- Preferences -- Preferences
if dt.configuration.running_os == "windows" then if dt.configuration.running_os == "windows" then
dt.preferences.register("kml_export", dt.preferences.register("kml_export",
@@ -322,7 +335,7 @@ local defaultDir = ''
if dt.configuration.running_os == "windows" then if dt.configuration.running_os == "windows" then
defaultDir = os.getenv("USERPROFILE") defaultDir = os.getenv("USERPROFILE")
elseif dt.configuration.running_os == "macos" then elseif dt.configuration.running_os == "macos" then
defaultDir = os.getenv("home") defaultDir = os.getenv("HOME")
else else
local handle = io.popen("xdg-user-dir DESKTOP") local handle = io.popen("xdg-user-dir DESKTOP")
defaultDir = handle:read() defaultDir = handle:read()
@@ -369,5 +382,9 @@ else
dt.register_storage("kml_export", _("KML/KMZ Export"), nil, create_kml_file) dt.register_storage("kml_export", _("KML/KMZ Export"), nil, create_kml_file)
end end
script_data.destroy = destroy
return script_data
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua; -- kate: hl Lua;

View File

@@ -20,7 +20,7 @@
--[[ --[[
PASSPORT CROPPING GUIDE PASSPORT CROPPING GUIDE
guides for cropping passport photos based on documents from the Finnish police 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. (https://poliisi.fi/documents/25235045/31329600/Passport-photograph-instructions-by-the-police-2020-EN-fixed.pdf/1eec2f4c-aed7-68e0-c112-0a8f25e0328d/Passport-photograph-instructions-by-the-police-2020-EN-fixed.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 INSTALLATION
* copy this file in $CONFIGDIR/lua/ where CONFIGDIR is your darktable configuration directory * copy this file in $CONFIGDIR/lua/ where CONFIGDIR is your darktable configuration directory

View File

@@ -41,7 +41,6 @@ format (all fields can be the empty string):
local dt = require "darktable" local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
local df = require "lib/dtutils.file" local df = require "lib/dtutils.file"
require "official/yield"
local gettext = dt.gettext local gettext = dt.gettext
@@ -57,7 +56,16 @@ if not df.check_if_bin_exists("pdflatex") then
return return
end end
du.check_min_api_version("4.0.0", "pdf_slideshow") du.check_min_api_version("7.0.0", "pdf_slideshow")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
dt.preferences.register dt.preferences.register
("pdf_slideshow","open with","string", ("pdf_slideshow","open with","string",
@@ -165,6 +173,10 @@ local function slide(latexfile,i,image,file)
my_write(latexfile,"\\end{figure}\n") my_write(latexfile,"\\end{figure}\n")
end end
local function destroy()
dt.destroy_storage("pdf_slideshow")
end
dt.register_storage("pdf_slideshow",_("pdf slideshow"), dt.register_storage("pdf_slideshow",_("pdf slideshow"),
nil, nil,
function(storage,image_table) function(storage,image_table)
@@ -244,5 +256,8 @@ dt.register_storage("pdf_slideshow",_("pdf slideshow"),
nil, nil,
widget) widget)
script_data.destroy = destroy
return script_data
-- --
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

505
contrib/photils.lua Normal file
View File

@@ -0,0 +1,505 @@
--[[ photils Auto Tagging plugin
copyright (c) 2020 Tobias Scheck
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/>.
--]]
--[[
A darktable plugin that tries to predict keywords based on the selected image.
This plugin uses photils-cli to handle this task. Photils-cli is an application
that passes the image through a neural network, classifies it, and extracts the
suggested tags. Everything happens offline without the need that your data are
sent over the internet.
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
* photils-cli - https://github.com/scheckmedia/photils-cli at the moment only
available for Linux and MacOS
USAGE
* require this script from your main lua file
To do this add this line to the file .config/darktable/luarc:
require "contrib/photils"
* Select an image
* Press "get tags"
* Select the tags you want from a list of suggestions
* Press "Attach .. Tags" to add the selected tags to your image
--]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local dtsys = require "lib/dtutils.system"
local MODULE_NAME = "photils"
du.check_min_api_version("7.0.0", MODULE_NAME)
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
local gettext = dt.gettext
gettext.bindtextdomain(MODULE_NAME,
dt.configuration.config_dir .. PS .. "lua" .. PS .. "locale" .. PS)
local exporter = dt.new_format("jpeg")
exporter.quality = 80
exporter.max_height = 224
exporter.max_width = 224
-- helper functions
local function _(msgid)
return gettext.dgettext(MODULE_NAME, msgid)
end
local function num_keys(tbl)
local num = 0
for _ in pairs(tbl) do num = num + 1 end
return num
end
local function has_key(tbl, value)
for k, _ in pairs(tbl) do
if k == value then
return true
end
end
return false
end
local photils_installed = df.check_if_bin_exists("photils-cli")
--[[
local state object
maybe per_page is a preference variable but I think 10
is a good value for the ui
]]
local PHOTILS = {
tags = {},
confidences = {},
page = 1,
per_page = 10,
selected_tags = {},
in_pagination = false,
tagged_image = "",
module_installed = false,
event_registered = false,
plugin_display_views = {
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100},
[dt.gui.views.darkroom] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 100}
},
}
local GUI = {
container = dt.new_widget("box") {
orientation = "vertical",
sensitive = true,
dt.new_widget("button") {
label = _("get tags"),
sensitive = photils_installed,
clicked_callback = function() PHOTILS.on_tags_clicked() end
},
reset_callback = function() PHOTILS.on_reset(true) end
},
stack = dt.new_widget("stack"),
prev_button = dt.new_widget("button") {
label = "<",
sensitive = false,
clicked_callback = function()
PHOTILS.page = PHOTILS.page - 1
PHOTILS.paginate()
end
},
next_button = dt.new_widget("button") {
label = ">",
sensitive = false,
clicked_callback = function()
PHOTILS.page = PHOTILS.page + 1
PHOTILS.paginate()
end
},
tag_box = dt.new_widget("box") {orientation = "vertical"},
tag_view = dt.new_widget("box") {orientation = "vertical"},
page_label = dt.new_widget("label") {label = ""},
error_view = dt.new_widget("box") {orientation = "vertical"},
warning_label = dt.new_widget("label") {
label = ""
},
restart_required_label = dt.new_widget("label") {
label = _("requires a restart to be applied")
},
attach_button = dt.new_widget("button") {
label = "",
sensitive = false,
clicked_callback = function(self) PHOTILS.attach_tags() end
},
confidence_slider = dt.new_widget("slider") {
step = 1,
digits = 0,
value = 90,
hard_max = 100,
hard_min = 0,
soft_max = 100,
soft_min = 0,
label = _("min confidence value")
},
warning = dt.new_widget("label")
}
function PHOTILS.image_changed()
local current_image = tostring(dt.gui.selection()[1])
if current_image ~= PHOTILS.tagged_image then
if PHOTILS.tagged_image ~= "" then
PHOTILS.tagged_image_has_changed()
end
PHOTILS.tagged_image = tostring(current_image)
end
end
function PHOTILS.tagged_image_has_changed()
GUI.warning.label = _("The suggested tags were not generated\n for the currently selected image!")
end
function PHOTILS.paginate()
PHOTILS.in_pagination = true
local num_pages = math.ceil(#PHOTILS.tags / PHOTILS.per_page)
GUI.page_label.label = string.format(_(" page %s of %s "), PHOTILS.page,
num_pages)
if PHOTILS.page <= 1 then
PHOTILS.page = 1
GUI.prev_button.sensitive = false
else
GUI.prev_button.sensitive = true
end
if PHOTILS.page > num_pages - 1 then
PHOTILS.page = num_pages
GUI.next_button.sensitive = false
else
GUI.next_button.sensitive = true
end
--[[
calculates the start positon in the tag array based on the current page
and takes N tags from that array to show these in darktable
e.g. page 1 goes from 1 to 10, page 2 from 11 to 20 a.s.o.
the paginaton approach is related to a problem with the dynamic addition
of mutliple widgets https://github.com/darktable-org/darktable/issues/4934#event-3318100463
]]--
local offset = ((PHOTILS.page - 1) * PHOTILS.per_page) + 1
local tag_index = 1
for i = offset, offset + PHOTILS.per_page - 1, 1 do
local tag = PHOTILS.tags[i]
local conf = PHOTILS.confidences[i]
GUI.tag_box[tag_index].value = has_key(PHOTILS.selected_tags, tag)
if tag then
if dt.preferences.read(MODULE_NAME, "show_confidence", "bool") then
tag = tag .. string.format(" (%.3f)", conf)
end
GUI.tag_box[tag_index].label = tag
GUI.tag_box[tag_index].sensitive = true
else
GUI.tag_box[tag_index].label = ""
GUI.tag_box[tag_index].sensitive = false
end
tag_index = tag_index + 1
end
PHOTILS.in_pagination = false
end
function PHOTILS.attach_tags()
local num_selected = #dt.gui.selection()
local job = dt.gui.create_job(_("Apply tag to image"), true)
for i = 1, num_selected, 1 do
local image = dt.gui.selection()[i]
for tag, _ in pairs(PHOTILS.selected_tags) do
local dt_tag = dt.tags.create(tag)
dt.tags.attach(dt_tag, image)
end
job.percent = i / num_selected
end
dt.print(_("Tags successfully attached to image"))
job.valid = false
end
function PHOTILS.get_tags(image, with_export)
local tmp_file = df.create_tmp_file()
local in_arg = df.sanitize_filename(tostring(image))
local out_arg = df.sanitize_filename(tmp_file)
local executable = photils_installed
if dt.configuration.running_os == "macos" then
executable = executable .. "/Contents/MacOS/photils-cli"
end
if with_export then
dt.print_log("use export to for prediction")
local export_file = df.create_tmp_file()
exporter:write_image(image, export_file)
in_arg = df.sanitize_filename(tostring(export_file))
end
local command = executable .. " -c " .. " -i " .. in_arg .. " -o " .. out_arg
local ret = dtsys.external_command(command)
if ret > 0 then
dt.print_error(string.format("command %s returned error code %d", command, ret))
os.remove(tmp_file)
-- try to export the image and run tagging
if not with_export then
return PHOTILS.get_tags(image, true)
end
return false
end
for i = #PHOTILS.tags, 1, -1 do
PHOTILS.tags[i] = nil
PHOTILS.confidences[i] = nil
end
for tag in io.lines(tmp_file) do
local splitted = du.split(tag, ":")
if 100 * tonumber(splitted[2]) >= GUI.confidence_slider.value then
PHOTILS.tags[#PHOTILS.tags + 1] = splitted[1]
PHOTILS.confidences[#PHOTILS.confidences+1] = splitted[2]
end
end
dt.print(string.format(_("%s found %d tags for your image"), MODULE_NAME,
#PHOTILS.tags))
os.remove(tmp_file)
return true
end
function PHOTILS.on_tags_clicked()
PHOTILS.page = 1
GUI.warning.label = ""
PHOTILS.on_reset(false)
local images = dt.gui.selection()
if #images == 0 then
dt.print(_("No image selected."))
dt.control.sleep(2000)
else
if #images > 1 then
dt.print(_("This plugin can only handle a single image."))
dt.gui.selection({images[1]})
dt.control.sleep(2000)
end
with_export = dt.preferences.read(MODULE_NAME, "export_image_before_for_tags", "bool")
if not PHOTILS.get_tags(images[1], with_export) then
local msg = string.format(_("%s failed, see terminal output for details"), MODULE_NAME)
GUI.warning_label.label = msg
GUI.stack.active = GUI.error_view
dt.print(msg)
return
end
if #PHOTILS.tags == 0 then
local msg = string.format(_("no tags where found"), MODULE_NAME)
GUI.warning_label.label = msg
GUI.stack.active = GUI.error_view
return
end
GUI.stack.active = GUI.tag_view
PHOTILS.paginate()
end
end
function PHOTILS.tag_selected(tag_button)
if PHOTILS.in_pagination then return end
if tag_button.value then
local tag = tag_button.label
if dt.preferences.read(MODULE_NAME, "show_confidence", "bool") then
local idx = string.find(tag, "%(") - 2
tag = string.sub(tag, 0, idx)
end
PHOTILS.selected_tags[tag] = tag
else
PHOTILS.selected_tags[tag_button.label] = nil
end
local num_selected = num_keys(PHOTILS.selected_tags)
if num_selected == 0 then
GUI.attach_button.label = ""
GUI.attach_button.sensitive = false
else
GUI.attach_button.label = string.format(_("attach %d tags"),
num_selected)
GUI.attach_button.sensitive = true
end
end
function PHOTILS.on_reset(with_view)
if with_view then GUI.stack.active = 1 end
for k, _ in pairs(PHOTILS.selected_tags) do
PHOTILS.selected_tags[k] = nil
end
for _, v in ipairs(GUI.tag_box) do
v.value = false
end
GUI.attach_button.label = ""
GUI.attach_button.sensitive = false
end
local function install_module()
if not PHOTILS.module_installed then
dt.register_lib(MODULE_NAME,
"photils autotagger",
true,
true,
PHOTILS.plugin_display_views,
GUI.container,
nil,
nil
)
PHOTILS.module_installed = true
end
end
local function destroy()
dt.gui.libs[MODULE_NAME].visible = false
dt.destroy_event("photils", "mouse-over-image-changed")
end
local function restart()
dt.gui.libs[MODULE_NAME].visible = true
dt.register_event("photils", "mouse-over-image-changed",
PHOTILS.image_changed)
end
local function show()
dt.gui.libs[MODULE_NAME].visible = true
end
-- add a fix number of buttons
for _ = 1, PHOTILS.per_page, 1 do
local btn_tag = dt.new_widget("check_button") {
label = "",
sensitive = false,
clicked_callback = PHOTILS.tag_selected
}
table.insert(GUI.tag_box, btn_tag)
end
if not photils_installed then
GUI.warning_label.label = _("photils-cli not found")
dt.print_log(_("photils-cli not found"))
else
GUI.warning_label.label = _("Select an image, click \"get tags\" and get \nsuggestions for tags.")
end
GUI.pagination = dt.new_widget("box") {
orientation = "horizontal",
GUI.prev_button,
GUI.page_label,
GUI.next_button
}
table.insert(GUI.error_view, GUI.warning_label)
if not photils_installed then
table.insert(GUI.error_view, df.executable_path_widget({"photils-cli"}))
table.insert(GUI.error_view, GUI.restart_required_label)
end
table.insert(GUI.stack, GUI.error_view)
table.insert(GUI.stack, GUI.tag_view)
table.insert(GUI.tag_view, GUI.pagination)
table.insert(GUI.tag_view, GUI.tag_box)
table.insert(GUI.tag_view, GUI.attach_button)
table.insert(GUI.tag_view, GUI.warning)
table.insert(GUI.container, GUI.confidence_slider)
table.insert(GUI.container, GUI.stack)
GUI.stack.active = 1
-- uses photils: prefix because script settings are all together and not seperated by script
dt.preferences.register(MODULE_NAME,
"show_confidence",
"bool",
_("photils: show confidence value"),
_("if enabled, the confidence value for each tag is displayed"),
true)
dt.preferences.register(MODULE_NAME,
"export_image_before_for_tags",
"bool",
_("photils: use exported image for tag request"),
_("If enabled, the image passed to photils for tag suggestion is based on the exported, already edited image. " ..
"Otherwise, the embedded thumbnail of the RAW file will be used for tag suggestion." ..
"The embedded thumbnail could speedup the tag suggestion but can fail if the RAW file is not supported."),
true)
dt.register_event("photils", "mouse-over-image-changed",
PHOTILS.image_changed)
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not PHOTILS.event_registered then
dt.register_event(
"photils", "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
PHOTILS.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = show
return script_data

View File

@@ -47,11 +47,23 @@ local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
local debug = require "darktable.debug" local debug = require "darktable.debug"
du.check_min_api_version("7.0.0", "quicktag")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
local qt = {}
qt.module_installed = false
qt.event_registered = false
qt.widget_table = {}
local gettext = dt.gettext local gettext = dt.gettext
du.check_min_api_version("3.0.0", "quicktag")
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("quicktag",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("quicktag",dt.configuration.config_dir.."/lua/locale/")
@@ -156,7 +168,7 @@ abbrevate_tags(quicktag_table)
local button = {} local button = {}
-- function to create buttons with tags as labels -- function to create buttons with tags as labels
for j=1,qnr do for j = 1, qnr do
button[#button+1] = dt.new_widget("button") { button[#button+1] = dt.new_widget("button") {
label = j..": "..quicktag_table[j], label = j..": "..quicktag_table[j],
clicked_callback = function() tagattach(tostring(quicktag_table[j]),j) end} clicked_callback = function() tagattach(tostring(quicktag_table[j]),j) end}
@@ -177,12 +189,56 @@ local function update_quicktag_list()
end end
end end
local function install_module()
if not qt.module_installed then
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(qt.widget_table),
},
nil,-- view_enter
nil -- view_leave
)
qt.module_installed = true
end
end
local function destroy()
dt.gui.libs["quicktag"].visible = false
for i=1,qnr do
dt.destroy_event("quicktag " .. tostring(i), "shortcut")
end
end
local function restart()
dt.gui.libs["quicktag"].visible = true
for i = 1 ,qnr do
dt.register_event("quicktag", "shortcut",
function(event, shortcut) tagattach(tostring(quicktag_table[i])) end,
string.format(_("quicktag %i"),i))
end
end
local function show()
dt.gui.libs["quicktag"].visible = true
end
update_quicktag_list() update_quicktag_list()
local new_quicktag = dt.new_widget("entry"){ local new_quicktag = dt.new_widget("entry"){
text = "", text = "",
placeholder = _("new tag"), placeholder = _("new tag"),
is_password = true, is_password = false,
editable = true, editable = true,
tooltip = _("enter your tag here") tooltip = _("enter your tag here")
} }
@@ -213,40 +269,46 @@ local new_qt_widget = dt.new_widget ("box") {
-- back UI elements in a table -- back UI elements in a table
-- thanks to wpferguson for the hint -- thanks to wpferguson for the hint
local widget_table = {}
for i=1,qnr do for i=1,qnr do
widget_table[#widget_table + 1] = button[i] qt.widget_table[#qt.widget_table + 1] = button[i]
end end
widget_table[#widget_table + 1] = dt.new_widget("separator"){} qt.widget_table[#qt.widget_table + 1] = dt.new_widget("separator"){}
widget_table[#widget_table + 1] = old_quicktag qt.widget_table[#qt.widget_table + 1] = old_quicktag
widget_table[#widget_table + 1] = new_qt_widget qt.widget_table[#qt.widget_table + 1] = new_qt_widget
--create module --create module
dt.register_lib( if dt.gui.current_view().id == "lighttable" then
"quicktag", -- Module name install_module()
"quicktag", -- name else
true, -- expandable if not qt.event_registered then
false, -- resetable dt.register_event(
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 490}}, "quicktag", "view-changed",
function(event, old_view, new_view)
dt.new_widget("box"){ if new_view.name == "lighttable" and old_view.name == "darkroom" then
orientation = "vertical", install_module()
table.unpack(widget_table), end
end
}, )
nil,-- view_enter qt.event_registered = true
nil -- view_leave end
) end
-- create shortcuts -- create shortcuts
for i=1,qnr do for i=1,qnr do
dt.register_event("shortcut", dt.register_event("quicktag " .. tostring(i), "shortcut",
function(event, shortcut) tagattach(tostring(quicktag_table[i])) end, function(event, shortcut) tagattach(tostring(quicktag_table[i])) end,
string.format(_("quicktag %i"),i)) string.format(_("quicktag %i"),i))
end end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = show
return script_data
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on; -- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;

View File

@@ -42,7 +42,16 @@ local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
-- added version check -- added version check
du.check_min_api_version("3.0.0", "rate_group") du.check_min_api_version("7.0.0", "rate_group")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local function apply_rating(rating) local function apply_rating(rating)
local images = dt.gui.action_images local images = dt.gui.action_images
@@ -59,30 +68,51 @@ local function apply_rating(rating)
end end
end end
dt.register_event("shortcut",function(event, shortcut) local function destroy()
dt.destroy_event("rg_reject", "shortcut")
dt.destroy_event("rg0", "shortcut")
dt.destroy_event("rg1", "shortcut")
dt.destroy_event("rg2", "shortcut")
dt.destroy_event("rg3", "shortcut")
dt.destroy_event("rg4", "shortcut")
dt.destroy_event("rg5", "shortcut")
end
dt.register_event("rg_reject", "shortcut",
function(event, shortcut)
apply_rating(-1) apply_rating(-1)
end, "Reject group") end, "Reject group")
dt.register_event("shortcut",function(event, shortcut) dt.register_event("rg0", "shortcut",
function(event, shortcut)
apply_rating(0) apply_rating(0)
end, "Rate group 0") end, "Rate group 0")
dt.register_event("shortcut",function(event, shortcut) dt.register_event("rg1", "shortcut",
function(event, shortcut)
apply_rating(1) apply_rating(1)
end, "Rate group 1") end, "Rate group 1")
dt.register_event("shortcut",function(event, shortcut) dt.register_event("rg2", "shortcut",
function(event, shortcut)
apply_rating(2) apply_rating(2)
end, "Rate group 2") end, "Rate group 2")
dt.register_event("shortcut",function(event, shortcut) dt.register_event("rg3", "shortcut",
function(event, shortcut)
apply_rating(3) apply_rating(3)
end, "Rate group 3") end, "Rate group 3")
dt.register_event("shortcut",function(event, shortcut) dt.register_event("rg4", "shortcut",
function(event, shortcut)
apply_rating(4) apply_rating(4)
end, "Rate group 4") end, "Rate group 4")
dt.register_event("shortcut",function(event, shortcut) dt.register_event("rg5", "shortcut",
function(event, shortcut)
apply_rating(5) apply_rating(5)
end, "Rate group 5") end, "Rate group 5")
script_data.destroy = destroy
return script_data

View File

@@ -33,7 +33,21 @@ local du = require "lib/dtutils"
local debug = require "darktable.debug" local debug = require "darktable.debug"
-- check API version -- check API version
du.check_min_api_version("3.0.0", "rename-tags") du.check_min_api_version("7.0.0", "rename-tags")
du.deprecated("contrib/rename-tags.lua","darktable release 4.0")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local rt = {}
rt.module_installed = false
rt.event_registered = false
-- GUI entries -- GUI entries
local old_tag = darktable.new_widget("entry") { tooltip = "Enter old tag" } local old_tag = darktable.new_widget("entry") { tooltip = "Enter old tag" }
@@ -99,6 +113,21 @@ local function rename_tags()
rename_reset() rename_reset()
end end
local function install_module()
if not rt.module_installed then
darktable.register_lib ("rename_tags", "rename tag", true, true, {[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 20},}, rt.rename_widget, nil, nil)
rt.module_installed = true
end
end
local function destroy()
darktable.gui.libs["rename_tags"].visible = false
end
local function restart()
darktable.gui.libs["rename_tags"].visible = true
end
-- GUI -- GUI
local old_widget = darktable.new_widget ("box") { local old_widget = darktable.new_widget ("box") {
orientation = "horizontal", orientation = "horizontal",
@@ -112,7 +141,7 @@ local new_widget = darktable.new_widget ("box") {
new_tag new_tag
} }
local rename_widget = darktable.new_widget ("box") { rt.rename_widget = darktable.new_widget ("box") {
orientation = "vertical", orientation = "vertical",
reset_callback = rename_reset, reset_callback = rename_reset,
old_widget, old_widget,
@@ -120,6 +149,27 @@ local rename_widget = darktable.new_widget ("box") {
darktable.new_widget("button") { label = "Go", clicked_callback = rename_tags } darktable.new_widget("button") { label = "Go", clicked_callback = rename_tags }
} }
if darktable.gui.current_view().id == "lighttable" then
install_module()
else
if not rt.event_registered then
darktable.register_event(
"rename_tags", "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
rt.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data
darktable.register_lib ("rename_tags", "rename tag", true, true, {[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 20},}, rename_widget, nil, nil)

353
contrib/rename_images.lua Normal file
View File

@@ -0,0 +1,353 @@
--[[
rename.lua - rename image file(s)
Copyright (C) 2020, 2021 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/>.
]]
--[[
rename - rename an image file or files
This shortcut resets the GPS information to that contained within
the image file. If no GPS info is in the image file, the GPS data
is cleared.
USAGE
* require this script from your luarc file or start it from script_manager
* select an image or images
* enter a renaming pattern
* click the button to rename the files
BUGS, COMMENTS, SUGGESTIONS
* Send to Bill Ferguson, wpferguson@gmail.com
CHANGES
TODO
* Add pattern builder
* Add new name preview
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
du.check_min_api_version("7.0.0", "rename_images")
local gettext = dt.gettext
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("rename_images",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("rename_images", msgid)
end
-- namespace variable
local rename = {
presets = {},
substitutes = {},
placeholders = {"ROLL_NAME","FILE_FOLDER","FILE_NAME","FILE_EXTENSION","ID","VERSION","SEQUENCE","YEAR","MONTH","DAY",
"HOUR","MINUTE","SECOND","EXIF_YEAR","EXIF_MONTH","EXIF_DAY","EXIF_HOUR","EXIF_MINUTE","EXIF_SECOND",
"STARS","LABELS","MAKER","MODEL","TITLE","CREATOR","PUBLISHER","RIGHTS","USERNAME","PICTURES_FOLDER",
"HOME","DESKTOP","EXIF_ISO","EXIF_EXPOSURE","EXIF_EXPOSURE_BIAS","EXIF_APERTURE","EXIF_FOCUS_DISTANCE",
"EXIF_FOCAL_LENGTH","LONGITUDE","LATITUDE","ELEVATION","LENS","DESCRIPTION","EXIF_CROP"},
widgets = {},
}
rename.module_installed = false
rename.event_registered = false
-- script_manager integration
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- - - - - - - - - - - - - - - - - - - - - - - -
-- C O N S T A N T S
-- - - - - - - - - - - - - - - - - - - - - - - -
local MODULE_NAME = "rename_images"
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
local USER = os.getenv("USERNAME")
local HOME = os.getenv(dt.configuration.running_os == "windows" and "HOMEPATH" or "HOME")
local PICTURES = HOME .. PS .. dt.configuration.running_os == "windows" and "My Pictures" or "Pictures"
local DESKTOP = HOME .. PS .. "Desktop"
-- - - - - - - - - - - - - - - - - - - - - - - -
-- F U N C T I O N S
-- - - - - - - - - - - - - - - - - - - - - - - -
local function build_substitution_list(image, sequence, datetime, username, pic_folder, home, desktop)
-- build the argument substitution list from each image
-- local datetime = os.date("*t")
local colorlabels = {}
if image.red then table.insert(colorlabels, "red") end
if image.yellow then table.insert(colorlabels, "yellow") end
if image.green then table.insert(colorlabels, "green") end
if image.blue then table.insert(colorlabels, "blue") end
if image.purple then table.insert(colorlabels, "purple") end
local labels = #colorlabels == 1 and colorlabels[1] or du.join(colorlabels, ",")
local eyear,emon,eday,ehour,emin,esec = string.match(image.exif_datetime_taken, "(%d-):(%d-):(%d-) (%d-):(%d-):(%d-)$")
local replacements = {image.film,
image.path,
df.get_filename(image.filename),
string.upper(df.get_filetype(image.filename)),
image.id,image.duplicate_index,
string.format("%04d", sequence),
datetime.year,
string.format("%02d", datetime.month),
string.format("%02d", datetime.day),
string.format("%02d", datetime.hour),
string.format("%02d", datetime.min),
string.format("%02d", datetime.sec),
eyear,
emon,
eday,
ehour,
emin,
esec,
image.rating,
labels,
image.exif_maker,
image.exif_model,
image.title,
image.creator,
image.publisher,
image.rights,
username,
pic_folder,
home,
desktop,
image.exif_iso,
image.exif_exposure,
image.exif_exposure_bias,
image.exif_aperture,
image.exif_focus_distance,
image.exif_focal_length,
image.longitude,
image.latitude,
image.elevation,
image.exif_lens,
image.description,
image.exif_crop
}
for i=1,#rename.placeholders,1 do rename.substitutes[rename.placeholders[i]] = replacements[i] end
end
local function substitute_list(str)
-- replace the substitution variables in a string
for match in string.gmatch(str, "%$%(.-%)") do
local var = string.match(match, "%$%((.-)%)")
if rename.substitutes[var] then
str = string.gsub(str, "%$%("..var.."%)", rename.substitutes[var])
else
dt.print_error(_("unrecognized variable " .. var))
dt.print(_("unknown variable " .. var .. ", aborting..."))
return -1
end
end
return str
end
local function clear_substitute_list()
for i=1,#rename.placeholders,1 do rename.substitutes[rename.placeholders[i]] = nil end
end
local function stop_job(job)
job.valid = false
end
local function install_module()
if not rename.module_installed then
dt.register_lib(
MODULE_NAME,
_("rename images"),
true,
true,
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",700}},
dt.new_widget("box"){
orientation = "vertical",
rename.widgets.pattern,
rename.widgets.button,
},
nil,
nil
)
rename.module_installed = true
end
end
local function destroy()
dt.gui.libs[MODULE_NAME].visible = false
end
local function restart()
dt.gui.libs[MODULE_NAME].visible = true
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- M A I N
-- - - - - - - - - - - - - - - - - - - - - - - -
local function do_rename(images)
if #images > 0 then
local pattern = rename.widgets.pattern.text
dt.preferences.write(MODULE_NAME, "pattern", "string", pattern)
dt.print_log(_("pattern is " .. pattern))
if string.len(pattern) > 0 then
local datetime = os.date("*t")
local job = dt.gui.create_job(_("renaming images"), true, stop_job)
for i, image in ipairs(images) do
if job.valid then
job.percent = i / #images
build_substitution_list(image, i, datetime, USER, PICTURES, HOME, DESKTOP)
local new_name = substitute_list(pattern)
if new_name == -1 then
dt.print(_("unable to do variable substitution, exiting..."))
stop_job(job)
return
end
clear_substitute_list()
local args = {}
local path = string.sub(df.get_path(new_name), 1, -2)
if string.len(path) == 0 then
path = image.path
end
local filename = df.get_filename(new_name)
local filmname = image.path
if path ~= image.path then
if not df.check_if_file_exists(df.sanitize_filename(path)) then
df.mkdir(df.sanitize_filename(path))
end
filmname = path
end
args[1] = dt.films.new(filmname)
args[2] = image
if filename ~= image.filename then
args[3] = filename
end
dt.database.move_image(table.unpack(args))
end
end
stop_job(job)
local collect_rules = dt.gui.libs.collect.filter()
dt.gui.libs.collect.filter(collect_rules)
dt.print(_("renamed " .. #images .. " images"))
else -- pattern length
dt.print_error("no pattern supplied, returning...")
dt.print(_("please enter the new name or pattern"))
end
else -- image count
dt.print_error("no images selected, returning...")
dt.print(_("please select some images and try again"))
end
end
local function reset_callback()
rename.widgets.pattern.text = ""
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- W I D G E T S
-- - - - - - - - - - - - - - - - - - - - - - - -
rename.widgets.pattern = dt.new_widget("entry"){
tooltip = _("$(ROLL_NAME) - film roll name\n") ..
_("$(FILE_FOLDER) - image file folder\n") ..
_("$(FILE_NAME) - image file name\n") ..
_("$(FILE_EXTENSION) - image file extension\n") ..
_("$(ID) - image id\n") ..
_("$(VERSION) - version number\n") ..
_("$(SEQUENCE) - sequence number of selection\n") ..
_("$(YEAR) - current year\n") ..
_("$(MONTH) - current month\n") ..
_("$(DAY) - current day\n") ..
_("$(HOUR) - current hour\n") ..
_("$(MINUTE) - current minute\n") ..
_("$(SECOND) - current second\n") ..
_("$(EXIF_YEAR) - EXIF year\n") ..
_("$(EXIF_MONTH) - EXIF month\n") ..
_("$(EXIF_DAY) - EXIF day\n") ..
_("$(EXIF_HOUR) - EXIF hour\n") ..
_("$(EXIF_MINUTE) - EXIF minute\n") ..
_("$(EXIF_SECOND) - EXIF seconds\n") ..
_("$(EXIF_ISO) - EXIF ISO\n") ..
_("$(EXIF_EXPOSURE) - EXIF exposure\n") ..
_("$(EXIF_EXPOSURE_BIAS) - EXIF exposure bias\n") ..
_("$(EXIF_APERTURE) - EXIF aperture\n") ..
_("$(EXIF_FOCAL_LENGTH) - EXIF focal length\n") ..
_("$(EXIF_FOCUS_DISTANCE) - EXIF focus distance\n") ..
_("$(EXIF_CROP) - EXIF crop\n") ..
_("$(LONGITUDE) - longitude\n") ..
_("$(LATITUDE) - latitude\n") ..
_("$(ELEVATION) - elevation\n") ..
_("$(STARS) - star rating\n") ..
_("$(LABELS) - color labels\n") ..
_("$(MAKER) - camera maker\n") ..
_("$(MODEL) - camera model\n") ..
_("$(LENS) - lens\n") ..
_("$(TITLE) - title from metadata\n") ..
_("$(DESCRIPTION) - description from metadata\n") ..
_("$(CREATOR) - creator from metadata\n") ..
_("$(PUBLISHER) - publisher from metadata\n") ..
_("$(RIGHTS) - rights from metadata\n") ..
_("$(USERNAME) - username\n") ..
_("$(PICTURES_FOLDER) - pictures folder\n") ..
_("$(HOME) - user's home directory\n") ..
_("$(DESKTOP) - desktop directory"),
placeholder = _("enter pattern $(FILE_FOLDER)/$(FILE_NAME)"),
text = ""
}
local pattern_pref = dt.preferences.read(MODULE_NAME, "pattern", "string")
if pattern_pref then
rename.widgets.pattern.text = pattern_pref
end
rename.widgets.button = dt.new_widget("button"){
label = _("rename"),
clicked_callback = function(this)
do_rename(dt.gui.action_images)
end
}
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not rename.event_registered then
dt.register_event(
"rename_images", "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
rename.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data

View File

@@ -0,0 +1,77 @@
--[[
This file is part of darktable,
copyright (c) 2023 Dirk Dittmar
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 non-existing images in the the currently worked on images, e.g. the ones selected by the collection module.
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
-- module name
local MODULE = "select_non_existing"
du.check_min_api_version("9.1.0", MODULE)
-- figure out the path separator
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
local gettext = dt.gettext
gettext.bindtextdomain(MODULE, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
local function _(msgid)
return gettext.dgettext(MODULE, msgid)
end
local function stop_job(job)
job.valid = false
end
local function select_nonexisting_images(event, images)
local selection = {}
local job = dt.gui.create_job(_("select non existing images"), true, stop_job)
for key,image in ipairs(images) do
if(job.valid) then
job.percent = (key - 1)/#images
local filepath = image.path..PS..image.filename
local file_exists = df.test_file(filepath, "e")
dt.print_log(filepath.." exists? => "..tostring(file_exists))
if (not file_exists) then
table.insert(selection, image)
end
else
break
end
end
stop_job(job)
return selection
end
local function destroy()
dt.gui.libs.select.destroy_selection(MODULE)
end
dt.gui.libs.select.register_selection(
MODULE,
_("select non existing"),
select_nonexisting_images,
_("select all non-existing images in the current images"))
local script_data = {}
script_data.destroy = destroy
return script_data

View File

@@ -22,7 +22,16 @@ local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
local gettext = dt.gettext local gettext = dt.gettext
du.check_min_api_version("3.0.0", "select_untagged") du.check_min_api_version("7.0.0", "select_untagged")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("select_untagged",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("select_untagged",dt.configuration.config_dir.."/lua/locale/")
@@ -35,23 +44,23 @@ local function stop_job(job)
job.valid = false job.valid = false
end end
local function select_untagged_images() local function select_untagged_images(event, images)
job = dt.gui.create_job(_("select untagged images"), true, stop_job) job = dt.gui.create_job(_("select untagged images"), true, stop_job)
local selection = {} local selection = {}
for key,image in ipairs(dt.collection) do for key,image in ipairs(images) do
if(job.valid) then if(job.valid) then
job.percent = (key-1)/#dt.collection job.percent = (key - 1)/#images
local tags = dt.tags.get_tags(image) local tags = dt.tags.get_tags(image)
local hasTags = false local hasTags = false
for _,tag in ipairs(tags) do for _,tag in ipairs(tags) do
if not string.match(tag.name,"darktable|") then if not string.match(tag.name, "darktable|") then
hasTags = true hasTags = true
end end
end end
if hasTags == false then if hasTags == false then
table.insert(selection,image) table.insert(selection, image)
end end
else else
break break
@@ -59,7 +68,19 @@ local function select_untagged_images()
end end
job.valid = false job.valid = false
dt.gui.selection(selection) -- return table of images to set the selection to
return selection
end end
dt.gui.libs.select.register_selection(_("select untagged"),select_untagged_images,_("select all images containing no tags or only tags added by darktable")) local function destroy()
dt.gui.libs.select.destroy_selection("select_untagged")
end
dt.gui.libs.select.register_selection(
"select_untagged", _("select untagged"),
select_untagged_images,
_("select all images containing no tags or only tags added by darktable"))
script_data.destroy = destroy
return script_data

View File

@@ -27,10 +27,18 @@ USAGE
local dt = require "darktable" local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
local df = require "lib/dtutils.file" local df = require "lib/dtutils.file"
require "official/yield"
local gettext = dt.gettext local gettext = dt.gettext
du.check_min_api_version("2.0.2", "slideshowMusic") du.check_min_api_version("7.0.0", "slideshowMusic")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- Tell gettext where to find the .mo file translating messages for a particular domain -- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("slideshowMusic",dt.configuration.config_dir.."/lua/locale/") gettext.bindtextdomain("slideshowMusic",dt.configuration.config_dir.."/lua/locale/")
@@ -68,6 +76,12 @@ local function playSlideshowMusic(_, old_view, new_view)
end end
end end
function destroy()
dt.destroy_event("slideshow_music", "view-changed")
dt.preferences.destroy("slideshowMusic", "SlideshowMusic")
dt.preferences.destroy("slideshowMusic", "PlaySlideshowMusic")
end
-- Preferences -- Preferences
dt.preferences.register("slideshowMusic", "SlideshowMusic", "file", _("Slideshow background music file"), "", "") dt.preferences.register("slideshowMusic", "SlideshowMusic", "file", _("Slideshow background music file"), "", "")
dt.preferences.register("slideshowMusic", dt.preferences.register("slideshowMusic",
@@ -77,4 +91,9 @@ dt.preferences.register("slideshowMusic",
_("Plays music with rhythmbox if a slideshow starts"), _("Plays music with rhythmbox if a slideshow starts"),
true) true)
-- Register -- Register
dt.register_event("view-changed",playSlideshowMusic) dt.register_event("slideshow_music", "view-changed",
playSlideshowMusic)
script_data.destroy = destroy
return script_data

402
contrib/transfer_hierarchy.lua Executable file
View File

@@ -0,0 +1,402 @@
--[[
TRANSFER HIERARCHY
Allows the moving or copying of images from one directory
tree to another, while preserving the existing hierarchy.
AUTHOR
August Schwerdfeger (august@schwerdfeger.name)
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
None.
USAGE
darktable's native operations for moving and copying images in
batches allow only one directory to be specified as the destination
for each batch. Those wanting to move or copy images from a _hierarchy_
of directories within darktable while preserving the directory structure,
must take the laborious step of performing the operation one individual
directory at a time.
This module allows the intact moving and copying of whole directory trees.
It was designed for the specific use case of rapidly transferring images
from a customary source (e.g., a staging directory on the local disk)
to a customary destination (e.g., a directory on a NAS device).
Instructions for operation:
1. Select the set of images you want to copy.
2. Click the "calculate" button. This will calculate the
lowest directory in the hierarchy that contains every selected
file (i.e., the common prefix of all the images' pathnames), and
write its path into the "existing root" text box.
3. If (a) you have specified the "customary source root" and "customary
destination root" preferences, and (b) the selected images are all
contained under the directory specified as the customary source
root, then the "root of destination" text box will also be
automatically filled out.
For example, suppose that you have specified '/home/user/Staging'
as your customary source root and '/mnt/storage' as your customary
destination root. If all selected images fell under the directory
'/home/user/Staging/2020/Roll0001', the "root of destination" would
be automatically filled out with '/mnt/storage/2020/Roll0001'.
But if all selected images fall under a directory outside the
specified customary source root (e.g., '/opt/other'), the "root
of destination" text box must be filled out manually.
It is also possible to edit the "root of destination" further once
it has been automatically filled out.
4. Click the "move" or "copy" button.
Before moving or copying any images, the module will first
replicate the necessary directory hierarchy by creating all
destination directories that do not already exist; should a
directory creation attempt fail, the operation will be
aborted, but any directories already created will not be
removed.
During the actual move/copy operation, the module transfers an
image by taking its path and replacing the string in the "existing
root" text box with that in the "root of destination" text box
(e.g., '/home/user/Staging/2020/Roll0001/DSC_0001.jpg' would be
transferred to '/mnt/storage/2020/Roll0001/DSC_0001.jpg').
LICENSE
LGPLv2+
]]
-- Header material: BEGIN
local darktable = require("darktable")
local dtutils = require("lib/dtutils")
local dtutils_file = require("lib/dtutils.file")
local dtutils_system = require("lib/dtutils.system")
local LIB_ID = "transfer_hierarchy"
dtutils.check_min_api_version("7.0.0", LIB_ID)
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local MKDIR_COMMAND = darktable.configuration.running_os == "windows" and "mkdir " or "mkdir -p "
local PATH_SEPARATOR = darktable.configuration.running_os == "windows" and "\\\\" or "/"
local PATH_SEGMENT_REGEX = "(" .. PATH_SEPARATOR .. "?)([^" .. PATH_SEPARATOR .. "]+)"
unpack = unpack or table.unpack
gmatch = string.gfind or string.gmatch
darktable.gettext.bindtextdomain(LIB_ID, darktable.configuration.config_dir .. PATH_SEPARATOR .. "lua" .. PATH_SEPARATOR .. "locale" .. PATH_SEPARATOR)
local function _(msgid)
return darktable.gettext.dgettext(LIB_ID, msgid)
end
-- Header material: END
-- Helper functions: BEGIN
local th = {}
th.module_installed = false
th.event_registered = false
local function pathExists(path)
local success, err, errno = os.rename(path, path)
if not success then
if errno == 13 then
return true
end
end
return success, err
end
local function pathIsDirectory(path)
return pathExists(path..PATH_SEPARATOR)
end
local function createDirectory(path)
local errorlevel = dtutils_system.external_command(MKDIR_COMMAND .. dtutils_file.sanitize_filename(path))
if errorlevel == 0 and pathIsDirectory(path) then
return path
else
return nil
end
end
local function install_module()
if not th.module_installed then
darktable.register_lib(LIB_ID,
"transfer hierarchy", true, true, {
[darktable.gui.views.lighttable] = { "DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 700 }
}, th.transfer_widget, nil, nil)
th.module_installed = true
end
end
-- Helper functions: END
-- Widgets and business logic: BEGIN
local sourceTextBox = darktable.new_widget("entry") {
tooltip = _("Lowest directory containing all selected images"),
editable = false
}
sourceTextBox.reset_callback = function() sourceTextBox.text = "" end
local destinationTextBox = darktable.new_widget("entry") {
text = ""
}
destinationTextBox.reset_callback = function() destinationTextBox.text = "" end
local function findRootPath(films)
local commonSegments = nil
local prefix = ""
for film, _ in pairs(films) do
local path = film.path
if commonSegments == nil then
commonSegments = {}
local firstMatchIndex = string.find(path, PATH_SEGMENT_REGEX)
if firstMatchIndex ~= nil then
prefix = string.sub(path, 1, firstMatchIndex-1)
end
string.gsub(path, PATH_SEGMENT_REGEX, function(w, x)
if w ~= "" then table.insert(commonSegments, w) end
table.insert(commonSegments, x)
end)
else
local matcher = gmatch(path, PATH_SEGMENT_REGEX)
local i = 1
while i < #commonSegments do
match, match2 = matcher()
if match == nil then
while i <= #commonSegments do
table.remove(commonSegments, #commonSegments)
end
break
elseif match ~= "" then
if commonSegments[i] ~= match then
while i <= #commonSegments do
table.remove(commonSegments, #commonSegments)
end
break
else
i = i+1
end
end
if match2 == nil or commonSegments[i] ~= match2 then
while i <= #commonSegments do
table.remove(commonSegments, #commonSegments)
end
break
else
i = i+1
end
end
end
end
if commonSegments == nil then
return prefix
end
if commonSegments[#commonSegments] == PATH_SEPARATOR then
table.remove(commonSegments, #commonSegments)
end
rv = prefix .. table.concat(commonSegments)
return rv
end
local function calculateRoot()
films = {}
for _,img in ipairs(darktable.gui.action_images) do
films[img.film] = true
end
return findRootPath(films), films
end
local function doCalculate()
local rootPath = calculateRoot()
if rootPath ~= nil then
sourceTextBox.text = rootPath
local sourceBase = darktable.preferences.read(LIB_ID, "source_base", "directory")
local destBase = darktable.preferences.read(LIB_ID, "destination_base", "directory")
if sourceBase ~= nil and sourceBase ~= "" and
destBase ~= nil and destBase ~= "" and
string.sub(rootPath, 1, #sourceBase) == sourceBase then
destinationTextBox.text = destBase .. string.sub(rootPath, #sourceBase+1)
end
end
end
local function stopTransfer(transferJob)
transferJob.valid = false
end
local function doTransfer(transferFunc)
rootPath, films = calculateRoot()
if rootPath ~= sourceTextBox.text then
darktable.print(_("transfer hierarchy: ERROR: existing root is out of sync -- click 'calculate' to update"))
return
end
if destinationTextBox.text == "" then
darktable.print(_("transfer hierarchy: ERROR: destination not specified"))
return
end
local sourceBase = sourceTextBox.text
local destBase = destinationTextBox.text
local destFilms = {}
for film, _ in pairs(films) do
films[film] = destBase .. string.sub(film.path, #sourceBase+1)
if not pathExists(films[film]) then
if createDirectory(films[film]) == nil then
darktable.print(_("transfer hierarchy: ERROR: could not create directory: " .. films[film]))
return
end
end
if not pathIsDirectory(films[film]) then
darktable.print(_("transfer hierarchy: ERROR: not a directory: " .. films[film]))
return
end
destFilms[film] = darktable.films.new(films[film])
if destFilms[film] == nil then
darktable.print(_("transfer hierarchy: ERROR: could not create film: " .. film.path))
end
end
local srcFilms = {}
for _,img in ipairs(darktable.gui.action_images) do
srcFilms[img] = img.film
end
local job = darktable.gui.create_job(string.format(_("transfer hierarchy") .. " (%d image" .. (#(darktable.gui.action_images) == 1 and "" or "s") .. ")", #(darktable.gui.action_images)), true, stopTransfer)
job.percent = 0.0
local pctIncrement = 1.0 / #(darktable.gui.action_images)
for _,img in ipairs(darktable.gui.action_images) do
if job.valid and img.film == srcFilms[img] then
destFilm = destFilms[img.film]
transferFunc(img, destFilm)
job.percent = job.percent + pctIncrement
end
end
job.valid = false
local filterRules = darktable.gui.libs.collect.filter()
darktable.gui.libs.collect.filter(filterRules)
end
local function doMove()
doTransfer(darktable.database.move_image)
end
local function doCopy()
doTransfer(darktable.database.copy_image)
end
local function destroy()
darktable.gui.libs[LIB_ID].visible = false
end
local function restart()
darktable.gui.libs[LIB_ID].visible = true
end
th.transfer_widget = darktable.new_widget("box") {
orientation = "vertical",
darktable.new_widget("button") {
label = _("calculate"),
clicked_callback = doCalculate
},
darktable.new_widget("label") {
label = _("existing root"),
halign = "start"
},
sourceTextBox,
darktable.new_widget("label") {
label = _("root of destination"),
halign = "start"
},
destinationTextBox,
darktable.new_widget("button") {
label = _("move"),
tooltip = "Move all selected images",
clicked_callback = doMove
},
darktable.new_widget("button") {
label = _("copy"),
tooltip = _("Copy all selected images"),
clicked_callback = doCopy
}
}
-- Widgets and business logic: END
-- Preferences: BEGIN
darktable.preferences.register(
LIB_ID,
"source_base",
"string",
"[transfer hierarchy] Customary source root",
"",
"")
darktable.preferences.register(
LIB_ID,
"destination_base",
"string",
"[transfer hierarchy] Customary destination root",
"",
"")
-- Preferences: END
if darktable.gui.current_view().id == "lighttable" then
install_module()
else
if not th.event_registered then
darktable.register_event(
LIB_ID, "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
th.event_registered = true
end
end
script_data.destroy = destroy
script_data.restart = restart
script_data.destroy_method = "hide"
script_data.show = restart
return script_data

View File

@@ -39,7 +39,16 @@ local df = require "lib/dtutils.file"
local dsys = require "lib/dtutils.system" local dsys = require "lib/dtutils.system"
local gettext = dt.gettext local gettext = dt.gettext
du.check_min_api_version("5.0.0") du.check_min_api_version("7.0.0", "video_ffmpeg")
-- return data structure for script_manager
local script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local MODULE_NAME = "video_ffmpeg" local MODULE_NAME = "video_ffmpeg"
@@ -96,7 +105,7 @@ local resolutions = {
} }
} }
local framerates = {"15", "16", "23.98", "24", "25", "29,97", "30", "48", "50", "59.94", "60"} local framerates = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "15", "16", "23.98", "24", "25", "29,97", "30", "48", "50", "59.94", "60", "120", "240", "300"}
local formats = { local formats = {
["AVI"] = { ["AVI"] = {
@@ -277,7 +286,7 @@ local defaultVideoDir = ''
if dt.configuration.running_os == "windows" then if dt.configuration.running_os == "windows" then
defaultVideoDir = os.getenv("USERPROFILE")..PS .."videos" defaultVideoDir = os.getenv("USERPROFILE")..PS .."videos"
elseif dt.configuration.running_os == "macos" then elseif dt.configuration.running_os == "macos" then
defaultVideoDir = os.getenv("home")..PS.."Videos" defaultVideoDir = os.getenv("HOME")..PS.."Videos"
else else
local handle = io.popen("xdg-user-dir VIDEOS") local handle = io.popen("xdg-user-dir VIDEOS")
defaultVideoDir = handle:read() defaultVideoDir = handle:read()
@@ -448,6 +457,12 @@ local function finalize_export(storage, images_table, extra_data)
df.rmdir(df.sanitize_filename(tmp_dir)) df.rmdir(df.sanitize_filename(tmp_dir))
end end
-- script_manager integration
local function destroy()
dt.destroy_storage("module_video_ffmpeg")
end
dt.register_storage( dt.register_storage(
"module_video_ffmpeg", "module_video_ffmpeg",
_(MODULE_NAME), _(MODULE_NAME),
@@ -458,3 +473,8 @@ dt.register_storage(
module_widget module_widget
) )
-- script_manager integration
script_data.destroy = destroy
return script_data

View File

@@ -1,124 +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 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 du = require "lib/dtutils"
local df = require "lib/dtutils.file"
require "official/yield"
local gettext = dt.gettext
du.check_min_api_version("2.0.1", "video_mencoder")
-- 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)
dt.print("video_mencoder is deprecated. Please use video_ffmpeg instead. Please see the log for more details.")
dt.print_log("The mencoder executable is no longer distributed as part of most distributions,")
dt.print_log("therefore video_mencoder is deprecated. video_ffmpeg should be used in it's place. video_mencoder")
dt.print_log("will be removed when darktable 2.8 is released.")
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)

View File

@@ -23,5 +23,19 @@ USAGE
]] ]]
local dt = require "darktable" local dt = require "darktable"
-- script_manager integration to allow a script to be removed
-- without restarting darktable
local function destroy()
-- nothing to destroy
end
local result = dt.configuration.api_version_string local result = dt.configuration.api_version_string
dt.print_error("API Version: "..result) dt.print_error("API Version: "..result)
-- set the destroy routine so that script_manager can call it when
-- it's time to destroy the script and then return the data to
-- script_manager
local script_data = {}
script_data.destroy = destroy
return script_data

121
examples/darkroom_demo.lua Normal file
View File

@@ -0,0 +1,121 @@
--[[
This file is part of darktable,
copyright (c) 2019 Bill Ferguson <wpferguson@gmail.com>
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
darkroom_demo - an example script demonstrating how to control image display in darkroom mode
darkroom_demo is an example script showing how to control the currently displayed image in
darkroom mode using lua.
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
* none
USAGE
* require this script from your main lua file
BUGS, COMMENTS, SUGGESTIONS
* Send to Bill Ferguson, wpferguson@gmail.com
CHANGES
]]
local dt = require "darktable"
local du = require "lib/dtutils"
-- - - - - - - - - - - - - - - - - - - - - - - -
-- V E R S I O N C H E C K
-- - - - - - - - - - - - - - - - - - - - - - - -
du.check_min_api_version("5.0.2", "darkroom_mode") -- darktable 3.0
-- script_manager integration to allow a script to be removed
-- without restarting darktable
local function destroy()
-- nothing to destroy
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- C O N S T A N T S
-- - - - - - - - - - - - - - - - - - - - - - - -
local MODULE_NAME = "darkroom"
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
-- - - - - - - - - - - - - - - - - - - - - - - -
-- T R A N S L A T I O N S
-- - - - - - - - - - - - - - - - - - - - - - - -
local gettext = dt.gettext
gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
local function _(msgid)
return gettext.dgettext(MODULE_NAME, msgid)
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- M A I N
-- - - - - - - - - - - - - - - - - - - - - - - -
-- alias dt.control.sleep to sleep
local sleep = dt.control.sleep
-- save the configuration
local current_view = dt.gui.current_view()
-- check that there is an image selected, otherwise we can't activate darkroom viewe
local images = dt.gui.action_images
dt.print_log(#images .. " images selected")
if not images or #images == 0 then
dt.print_log("no images selected, creating selection")
dt.print_log("using image " .. dt.collection[1].filename)
dt.gui.selection({dt.collection[1]})
end
-- enter darkroom view
dt.gui.current_view(dt.gui.views.darkroom)
local max_images = 10
dt.print(_("Showing images, with a pause in between each"))
sleep(1500)
-- display first 10 images of collection pausing for a second between each
for i, img in ipairs(dt.collection) do
dt.print(_("Displaying image " .. i))
dt.gui.views.darkroom.display_image(img)
sleep(1500)
if i == max_images then
break
end
end
-- return to lighttable view
dt.print(_("Restoring view"))
sleep(1500)
dt.gui.current_view(current_view)
-- set the destroy routine so that script_manager can call it when
-- it's time to destroy the script and then return the data to
-- script_manager
local script_data = {}
script_data.destroy = destroy
return script_data

View File

@@ -58,6 +58,12 @@ local du = require "lib/dtutils"
--check API version --check API version
du.check_min_api_version("3.0.0", "gettextExample") du.check_min_api_version("3.0.0", "gettextExample")
-- script_manager integration to allow a script to be removed
-- without restarting darktable
local function destroy()
-- nothing to destroy
end
-- Not translated Text -- Not translated Text
dt.print_error("Hello World!") dt.print_error("Hello World!")
@@ -77,3 +83,11 @@ local function _(msgid)
return gettext.dgettext("gettextExample", msgid) return gettext.dgettext("gettextExample", msgid)
end end
dt.print_error(_('Hello World!')) dt.print_error(_('Hello World!'))
-- set the destroy routine so that script_manager can call it when
-- it's time to destroy the script and then return the data to
-- script_manager
local script_data = {}
script_data.destroy = destroy
return script_data

View File

@@ -31,7 +31,20 @@ local du = require "lib/dtutils"
du.check_min_api_version("2.0.0", "hello_world") du.check_min_api_version("2.0.0", "hello_world")
-- script_manager integration to allow a script to be removed
-- without restarting darktable
local function destroy()
-- nothing to destroy
end
dt.print("hello, world") dt.print("hello, world")
-- set the destroy routine so that script_manager can call it when
-- it's time to destroy the script and then return the data to
-- script_manager
local script_data = {}
script_data.destroy = destroy
return script_data
-- --
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

View File

@@ -0,0 +1,220 @@
--[[
This file is part of darktable,
copyright (c) 2019 Bill Ferguson <wpferguson@gmail.com>
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
lighttable_demo - an example script demonstrating how to control lighttable display modes
lighttable_demo is an example script showing how to control lighttable layout, sorting, and
filtering from a lua script. If the selected directory has different ratings, color labels, etc,
then the sorting and filtering display is a little clearer.
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
* none
USAGE
* require this script from your main lua file
BUGS, COMMENTS, SUGGESTIONS
* Send to Bill Ferguson, wpferguson@gmail.com
CHANGES
]]
local dt = require "darktable"
local du = require "lib/dtutils"
-- - - - - - - - - - - - - - - - - - - - - - - -
-- V E R S I O N C H E C K
-- - - - - - - - - - - - - - - - - - - - - - - -
du.check_min_api_version("5.0.2", "lighttable_demo") -- darktable 3.0
-- - - - - - - - - - - - - - - - - - - - - - - -
-- C O N S T A N T S
-- - - - - - - - - - - - - - - - - - - - - - - -
local MODULE_NAME = "lighttable"
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
-- script_manager integration to allow a script to be removed
-- without restarting darktable
local function destroy()
-- nothing to destroy
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- T R A N S L A T I O N S
-- - - - - - - - - - - - - - - - - - - - - - - -
local gettext = dt.gettext
gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
local function _(msgid)
return gettext.dgettext(MODULE_NAME, msgid)
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- F U N C T I O N S
-- - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - -
-- M A I N
-- - - - - - - - - - - - - - - - - - - - - - - -
-- alias dt.control.sleep to sleep
local sleep = dt.control.sleep
local layouts = {
"DT_LIGHTTABLE_LAYOUT_ZOOMABLE",
"DT_LIGHTTABLE_LAYOUT_FILEMANAGER",
"DT_LIGHTTABLE_LAYOUT_CULLING",
"DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC",
}
local sorts = {
"DT_COLLECTION_SORT_NONE",
"DT_COLLECTION_SORT_FILENAME",
"DT_COLLECTION_SORT_DATETIME",
"DT_COLLECTION_SORT_RATING",
"DT_COLLECTION_SORT_ID",
"DT_COLLECTION_SORT_COLOR",
"DT_COLLECTION_SORT_GROUP",
"DT_COLLECTION_SORT_PATH",
"DT_COLLECTION_SORT_CUSTOM_ORDER",
"DT_COLLECTION_SORT_TITLE",
"DT_COLLECTION_SORT_DESCRIPTION",
"DT_COLLECTION_SORT_ASPECT_RATIO",
"DT_COLLECTION_SORT_SHUFFLE"
}
local sort_orders = {
"DT_COLLECTION_SORT_ORDER_ASCENDING",
"DT_COLLECTION_SORT_ORDER_DESCENDING"
}
local ratings = {
"DT_COLLECTION_FILTER_ALL",
"DT_COLLECTION_FILTER_STAR_NO",
"DT_COLLECTION_FILTER_STAR_1",
"DT_COLLECTION_FILTER_STAR_2",
"DT_COLLECTION_FILTER_STAR_3",
"DT_COLLECTION_FILTER_STAR_4",
"DT_COLLECTION_FILTER_STAR_5",
"DT_COLLECTION_FILTER_REJECT",
"DT_COLLECTION_FILTER_NOT_REJECT"
}
local rating_comparators = {
"DT_COLLECTION_RATING_COMP_LT",
"DT_COLLECTION_RATING_COMP_LEQ",
"DT_COLLECTION_RATING_COMP_EQ",
"DT_COLLECTION_RATING_COMP_GEQ",
"DT_COLLECTION_RATING_COMP_GT",
"DT_COLLECTION_RATING_COMP_NE"
}
local zoom_levels = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
-- save filter, view, and collection parameters
local current_layout = dt.gui.libs.lighttable_mode.layout()
local current_zoom_level = dt.gui.libs.lighttable_mode.zoom_level()
local current_rating = dt.gui.libs.filter.rating()
local current_rating_comparator = dt.gui.libs.filter.rating_comparator()
local current_sort = dt.gui.libs.filter.sort()
local current_sort_order = dt.gui.libs.filter.sort_order()
-- cycle through layouts and zooms
dt.print(_("lighttable layout and zoom level demonstration"))
sleep(2000)
for n, layout in ipairs(layouts) do
dt.gui.libs.lighttable_mode.layout(layout)
dt.print(_("set lighttable layout to " .. layout))
dt.print_log(_("set lighttable layout to " .. layout))
sleep(1500)
for i = 1, 10 do
dt.gui.libs.lighttable_mode.zoom_level(i)
dt.print(_("Set zoom level to " .. i))
sleep(1500)
end
for i = 9, 1, -1 do
dt.gui.libs.lighttable_mode.zoom_level(i)
dt.print(_("Set zoom level to " .. i))
sleep(1500)
end
end
dt.print_log("finished layout and zoom level testing")
dt.print_log("starting sort demonstration")
-- cycle through sorts
dt.print(_("lighttable sorting demonstration"))
dt.print_log("setting lighttable to filemanager mode")
dt.gui.libs.lighttable_mode.layout("DT_LIGHTTABLE_LAYOUT_FILEMANAGER")
sleep(500)
dt.print_log("setting lighttable to zoom level 5")
dt.gui.libs.lighttable_mode.zoom_level(5)
dt.print_log("starting sorts")
for n, sort in ipairs(sorts) do
dt.gui.libs.filter.sort(sort)
dt.print(_("set lighttable sort to " .. sort))
sleep(1500)
for m, sort_order in ipairs(sort_orders) do
dt.gui.libs.filter.sort_order(sort_order)
dt.print(_("sort order set to " .. sort_order))
sleep(1500)
end
end
-- cycle through filters
dt.print(_("lighttable filtering demonstration"))
for n, rating in ipairs(ratings) do
dt.gui.libs.filter.rating(rating)
dt.print(_("set filter to " .. rating))
sleep(1500)
for m, rating_comparator in ipairs(rating_comparators) do
dt.gui.libs.filter.rating_comparator(rating_comparator)
dt.print(_("set rating comparator to " .. rating_comparator))
sleep(1500)
end
end
-- restore settings
dt.print(_("restoring settings"))
current_layout = dt.gui.libs.lighttable_mode.layout(current_layout)
current_zoom_level = dt.gui.libs.lighttable_mode.zoom_level(current_zoom_level)
current_rating = dt.gui.libs.filter.rating(current_rating)
current_rating_comparator = dt.gui.libs.filter.rating_comparator(current_rating_comparator)
current_sort =dt.gui.libs.filter.sort(current_sort)
current_sort_order = dt.gui.libs.filter.sort_order(current_sort_order)
-- set the destroy routine so that script_manager can call it when
-- it's time to destroy the script and then return the data to
-- script_manager
local script_data = {}
script_data.destroy = destroy
return script_data

View File

@@ -18,9 +18,9 @@
--[[ --[[
USAGE USAGE
* require this script from your main lua file * require this script from your luarc file
To do this add this line to the file .config/darktable/luarc: To do this add this line to the file .config/darktable/luarc:
require "moduleExample" require "examples/moduleExample"
* it creates a new example lighttable module * it creates a new example lighttable module
@@ -33,37 +33,113 @@ https://www.darktable.org/lua-api/index.html.php#darktable_new_widget
local dt = require "darktable" local dt = require "darktable"
local du = require "lib/dtutils" local du = require "lib/dtutils"
du.check_min_api_version("3.0.0", "moduleExample") du.check_min_api_version("7.0.0", "moduleExample")
-- add a new lib -- return data structure for script_manager
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 script_data = {}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
-- translation
-- https://www.darktable.org/lua-api/index.html#darktable_gettext
local gettext = dt.gettext
gettext.bindtextdomain("moduleExample", dt.configuration.config_dir .. "/lua/locale/")
local function _(msgid)
return gettext.dgettext("moduleExample", msgid)
end
-- declare a local namespace and a couple of variables we'll need to install the module
local mE = {}
mE.widgets = {}
mE.event_registered = false -- keep track of whether we've added an event callback or not
mE.module_installed = false -- keep track of whether the module is module_installed
--[[ We have to create the module in one of two ways depending on which view darktable starts
in. In orker to not repeat code, we wrap the darktable.register_lib in a local function.
]]
local function install_module()
if not mE.module_installed then
-- https://www.darktable.org/lua-api/index.html#darktable_register_lib
dt.register_lib(
"exampleModule", -- Module name
"exampleModule", -- name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
-- https://www.darktable.org/lua-api/types_lua_box.html
dt.new_widget("box") -- widget
{
orientation = "vertical",
dt.new_widget("button")
{
label = _("MyButton"),
clicked_callback = function (_)
dt.print(_("Button clicked"))
end
},
table.unpack(mE.widgets),
},
nil,-- view_enter
nil -- view_leave
)
mE.module_installed = true
end
end
-- script_manager integration to allow a script to be removed
-- without restarting darktable
local function destroy()
dt.gui.libs["exampleModule"].visible = false -- we haven't figured out how to destroy it yet, so we hide it for now
end
local function restart()
dt.gui.libs["exampleModule"].visible = true -- the user wants to use it again, so we just make it visible and it shows up in the UI
end
-- https://www.darktable.org/lua-api/types_lua_check_button.html
local check_button = dt.new_widget("check_button"){label = _("MyCheck_button"), value = true}
-- https://www.darktable.org/lua-api/types_lua_combobox.html
local combobox = dt.new_widget("combobox"){label = _("MyCombobox"), value = 2, "8", "16", "32"}
-- https://www.darktable.org/lua-api/types_lua_entry.html
local entry = dt.new_widget("entry") local entry = dt.new_widget("entry")
{ {
text = "test", text = "test",
placeholder = "placeholder", placeholder = _("placeholder"),
is_password = false, is_password = false,
editable = true, editable = true,
tooltip = "Tooltip Text", tooltip = _("Tooltip Text"),
reset_callback = function(self) self.text = "text" end reset_callback = function(self) self.text = "text" end
} }
-- https://www.darktable.org/lua-api/types_lua_file_chooser_button.html
local file_chooser_button = dt.new_widget("file_chooser_button") local file_chooser_button = dt.new_widget("file_chooser_button")
{ {
title = "MyFile_chooser_button", -- The title of the window when choosing a file title = _("MyFile_chooser_button"), -- The title of the window when choosing a file
value = "", -- The currently selected file value = "", -- The currently selected file
is_directory = false -- True if the file chooser button only allows directories to be selecte is_directory = false -- True if the file chooser button only allows directories to be selecte
} }
-- https://www.darktable.org/lua-api/types_lua_label.html
local label = dt.new_widget("label") local label = dt.new_widget("label")
label.label = "MyLabel" -- This is an alternative way to the "{}" syntax to set a property label.label = _("MyLabel") -- This is an alternative way to the "{}" syntax to set a property
-- https://www.darktable.org/lua-api/types_lua_separator.html
local separator = dt.new_widget("separator"){} local separator = dt.new_widget("separator"){}
-- https://www.darktable.org/lua-api/types_lua_slider.html
local slider = dt.new_widget("slider") local slider = dt.new_widget("slider")
{ {
label = "MySlider", label = _("MySlider"),
soft_min = 10, -- The soft minimum value for the slider, the slider can't go beyond this point 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 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_min = 0, -- The hard minimum value for the slider, the user can't manually enter a value beyond this point
@@ -71,69 +147,44 @@ local slider = dt.new_widget("slider")
value = 52 -- The current value of the slider value = 52 -- The current value of the slider
} }
if (dt.configuration.api_version_major >= 6) then -- pack the widgets in a table for loading in the module
local section_label = dt.new_widget("section_label")
{
label = "MySectionLabel"
}
dt.register_lib( table.insert(mE.widgets, check_button)
"exampleModule", -- Module name table.insert(mE.widgets, combobox)
"exampleModule", -- name table.insert(mE.widgets, entry)
true, -- expandable table.insert(mE.widgets, file_chooser_button)
false, -- resetable table.insert(mE.widgets, label)
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers table.insert(mE.widgets, separator)
dt.new_widget("box") -- widget table.insert(mE.widgets, slider)
{
orientation = "vertical", -- ... and tell dt about it all
dt.new_widget("button")
{
label = "MyButton", if dt.gui.current_view().id == "lighttable" then -- make sure we are in lighttable view
clicked_callback = function (_) install_module() -- register the lib
dt.print("Button clicked")
end
},
check_button,
combobox,
entry,
file_chooser_button,
label,
separator,
slider,
section_label
},
nil,-- view_enter
nil -- view_leave
)
else else
dt.register_lib( if not mE.event_registered then -- if we are not in lighttable view then register an event to signal when we might be
"exampleModule", -- Module name -- https://www.darktable.org/lua-api/index.html#darktable_register_event
"exampleModule", -- name dt.register_event(
true, -- expandable "mdouleExample", "view-changed", -- we want to be informed when the view changes
false, -- resetable function(event, old_view, new_view)
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers if new_view.name == "lighttable" and old_view.name == "darkroom" then -- if the view changes from darkroom to lighttable
dt.new_widget("box") -- widget install_module() -- register the lib
{ end
orientation = "vertical", end
dt.new_widget("button") )
{ mE.event_registered = true -- keep track of whether we have an event handler installed
label = "MyButton", end
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 end
-- set the destroy routine so that script_manager can call it when
-- it's time to destroy the script and then return the data to
-- script_manager
script_data.destroy = destroy
script_data.restart = restart -- only required for lib modules until we figure out how to destroy them
script_data.destroy_method = "hide" -- tell script_manager that we are hiding the lib so it knows to use the restart function
script_data.show = restart -- if the script was "off" when darktable exited, the module is hidden, so force it to show on start
return script_data
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua; -- kate: hl Lua;

View File

@@ -86,7 +86,7 @@ end
screen stating that you couldn't load because the minimum api version wasn't met. screen stating that you couldn't load because the minimum api version wasn't met.
]] ]]
du.check_min_api_version("5.0.0", "multi_os") du.check_min_api_version("7.0.0", "multi_os")
--[[ --[[
copy_image_attributes is a local subroutine to copy image attributes in the database from the raw image copy_image_attributes is a local subroutine to copy image attributes in the database from the raw image
@@ -196,6 +196,15 @@ local function extract_embedded_jpeg(images)
end end
end end
--[[
script_manager integration to allow a script to be removed
without restarting darktable
]]
local function destroy()
dt.destroy_event("multi_os", "shortcut") -- destroy the event since the callback will no longer be present
dt.gui.libs.image.destroy_action("multi_os") -- remove the button from the selected images module
end
--[[ --[[
Windows and MacOS don't place executables in the user's path so their location needs to be specified Windows and MacOS don't place executables in the user's path so their location needs to be specified
so that the script can find them. An exception to this is packages installed on MacOS with homebrew. Those so that the script can find them. An exception to this is packages installed on MacOS with homebrew. Those
@@ -204,41 +213,54 @@ end
to see if the executable is there. to see if the executable is there.
]] ]]
local executable = "ufraw-batch" if dt.configuration.running_os ~= "linux" then
local ufraw_batch_path_widget = dt.new_widget("file_chooser_button"){ local executable = "ufraw-batch"
title = _("Select ufraw-batch[.exe] executable"), local ufraw_batch_path_widget = dt.new_widget("file_chooser_button"){
value = df.get_executable_path_preference(executable), title = _("Select ufraw-batch[.exe] executable"),
is_directory = false, value = df.get_executable_path_preference(executable),
changed_callback = function(self) is_directory = false,
if df.check_if_bin_exists(self.value) then changed_callback = function(self)
df.set_executable_path_preference(executable, self.value) if df.check_if_bin_exists(self.value) then
df.set_executable_path_preference(executable, self.value)
end
end end
end }
} dt.preferences.register("executable_paths", "ufraw-batch", -- name
dt.preferences.register("executable_paths", "ufraw-batch", -- name "file", -- type
"file", -- type _('multi_os: ufraw-batch location'), -- label
_('multi_os: ufraw-batch location'), -- label _('Installed location of ufraw-batch. Requires restart to take effect.'), -- tooltip
_('Installed location of ufraw-batch. Requires restart to take effect.'), -- tooltip "ufraw-batch", -- default
"ufraw-batch", -- default ufraw_batch_path_widget
ufraw_batch_path_widget )
) end
--[[ --[[
Add a button to the selected images module in lighttable Add a button to the selected images module in lighttable
]] ]]
dt.gui.libs.image.register_action( dt.gui.libs.image.register_action(
_("extract embedded jpeg"), "multi_os", _("extract embedded jpeg"),
function(event, images) extract_embedded_jpeg(images) end, function(event, images) extract_embedded_jpeg(images) end,
"extract embedded jpeg" "extract embedded jpeg"
) )
--[[ --[[
Add a shortcut Add a shortcut
]] ]]
dt.register_event( dt.register_event(
"shortcut", "multi_os", "shortcut",
function(event, shortcut) extract_embedded_jpeg(dt.gui.action_images) end, function(event, shortcut) extract_embedded_jpeg(dt.gui.action_images) end,
"extract embedded jpeg" "extract embedded jpeg"
) )
--[[
set the destroy routine so that script_manager can call it when
it's time to destroy the script and then return the data to
script_manager
]]
local script_data = {}
script_data.destroy = destroy
return script_data

150
examples/panels_demo.lua Normal file
View File

@@ -0,0 +1,150 @@
--[[
This file is part of darktable,
copyright (c) 2019 Bill Ferguson <wpferguson@gmail.com>
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
panels_demo - an example script demonstrating how to contol panel visibility
panels_demo is an example script showing how to control panel visibility. It cycles
through the panels hiding them one by one, then showing them one by one, then
hiding all, then showing all. Finally, the original panel visibility is restored.
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
* none
USAGE
* require this script from your main lua file
BUGS, COMMENTS, SUGGESTIONS
* Send to Bill Ferguson, wpferguson@gmail.com
CHANGES
]]
local dt = require "darktable"
local du = require "lib/dtutils"
-- - - - - - - - - - - - - - - - - - - - - - - -
-- V E R S I O N C H E C K
-- - - - - - - - - - - - - - - - - - - - - - - -
du.check_min_api_version("7.0.0", "panels_demo")
-- script_manager integration to allow a script to be removed
-- without restarting darktable
local function destroy()
-- nothing to destroy
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- C O N S T A N T S
-- - - - - - - - - - - - - - - - - - - - - - - -
local MODULE_NAME = "panels"
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
-- - - - - - - - - - - - - - - - - - - - - - - -
-- T R A N S L A T I O N S
-- - - - - - - - - - - - - - - - - - - - - - - -
local gettext = dt.gettext
gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
local function _(msgid)
return gettext.dgettext(MODULE_NAME, msgid)
end
-- - - - - - - - - - - - - - - - - - - - - - - -
-- M A I N
-- - - - - - - - - - - - - - - - - - - - - - - -
-- alias dt.control.sleep to sleep
local sleep = dt.control.sleep
local panels = {"DT_UI_PANEL_CENTER_TOP", -- center top panel
"DT_UI_PANEL_CENTER_BOTTOM", -- center bottom panel
"DT_UI_PANEL_TOP", -- complete top panel
"DT_UI_PANEL_LEFT", -- left panel
"DT_UI_PANEL_RIGHT", -- right panel
"DT_UI_PANEL_BOTTOM"} -- complete bottom panel
local panel_status = {}
-- save panel visibility
for i = 1,#panels do
panel_status[i] = dt.gui.panel_visible(panels[i])
end
-- show all just in case
dt.gui.panel_show_all()
-- hide center_top, center_bottom, left, top, right, bottom in order
dt.print(_("Hiding all panels, one at a tme"))
sleep(1500)
for i = 1, #panels do
dt.print(_("Hiding " .. panels[i]))
dt.gui.panel_hide(panels[i])
sleep(1500)
end
-- display left, then top, then right, then bottom
dt.print(_("Make panels visible, one at a time"))
sleep(1500)
for i = #panels, 1, -1 do
dt.print(_("Showing " .. panels[i]))
dt.gui.panel_show(panels[i])
sleep(1500)
end
-- hide all
dt.print(_("Hiding all panels"))
sleep(1500)
dt.gui.panel_hide_all()
sleep(1500)
-- show all
dt.print(_("Showing all panels"))
sleep(1500)
dt.gui.panel_show_all()
sleep(1500)
-- restore
dt.print(_("Restoring panels to starting configuration"))
for i = 1, #panels do
if panel_status[i] then
dt.gui.panel_show(panels[i])
else
dt.gui.panel_hide(panels[i])
end
end
-- set the destroy routine so that script_manager can call it when
-- it's time to destroy the script and then return the data to
-- script_manager
local script_data = {}
script_data.destroy = destroy
return script_data

View File

@@ -26,6 +26,7 @@ local du = require "lib/dtutils"
du.check_min_api_version("2.0.1", "preferenceExamples") du.check_min_api_version("2.0.1", "preferenceExamples")
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. 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 "preferenceExamplesString", -- name
"string", -- type "string", -- type
@@ -34,14 +35,14 @@ dt.preferences.register("preferenceExamples", -- script: This is a string
"String") -- default "String") -- default
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. 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 "preferenceExamplesBool", -- name
"bool", -- type "bool", -- type
"Example Boolean", -- label "Example Boolean", -- label
"Example Boolean Tooltip", -- tooltip "Example Boolean Tooltip", -- tooltip
true) -- default true) -- default
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. 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 "preferenceExamplesInteger", -- name
"integer", -- type "integer", -- type
"Example Integer", -- label "Example Integer", -- label
"Example Integer Tooltip", -- tooltip "Example Integer Tooltip", -- tooltip
@@ -50,7 +51,7 @@ dt.preferences.register("preferenceExamples", -- script: This is a string
99) -- max 99) -- max
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. 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 "preferenceExamplesFloat", -- name
"float", -- type "float", -- type
"Example Float", -- label "Example Float", -- label
"Example Float Tooltip", -- tooltip "Example Float Tooltip", -- tooltip
@@ -60,21 +61,21 @@ dt.preferences.register("preferenceExamples", -- script: This is a string
0.5) -- step 0.5) -- step
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. 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 "preferenceExamplesFile", -- name
"file", -- type "file", -- type
"Example File", -- label "Example File", -- label
"Example File Tooltip", -- tooltip "Example File Tooltip", -- tooltip
"") -- default "") -- default
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. 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 "preferenceExamplesDirectory", -- name
"directory", -- type "directory", -- type
"Example Directory", -- label "Example Directory", -- label
"Example Directory Tooltip", -- tooltip "Example Directory Tooltip", -- tooltip
"") -- default "") -- default
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. 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 "preferenceExamplesEnum", -- name
"enum", -- type "enum", -- type
"Example Enum", -- label "Example Enum", -- label
"Example Enum Tooltip", -- tooltip "Example Enum Tooltip", -- tooltip

View File

@@ -24,6 +24,12 @@ local du = require "lib/dtutils"
du.check_min_api_version("5.0.0", "printExamples") du.check_min_api_version("5.0.0", "printExamples")
-- script_manager integration to allow a script to be removed
-- without restarting darktable
local function destroy()
-- nothing to destroy
end
-- Will print a string to the darktable control log (the long -- Will print a string to the darktable control log (the long
-- overlayed window that appears over the main panel). -- overlayed window that appears over the main panel).
dt.print("print") dt.print("print")
@@ -38,5 +44,12 @@ dt.print_error("print error")
-- to enable the Lua logdomain. -- to enable the Lua logdomain.
dt.print_log("print log") dt.print_log("print log")
-- set the destroy routine so that script_manager can call it when
-- it's time to destroy the script and then return the data to
-- script_manager
local script_data = {}
script_data.destroy = destroy
return script_data
-- --
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

View File

@@ -30,7 +30,21 @@ local du = require "lib/dtutils"
du.check_min_api_version("5.0.0", "running_os") du.check_min_api_version("5.0.0", "running_os")
-- script_manager integration to allow a script to be removed
-- without restarting darktable
local function destroy()
-- nothing to destroy
end
dt.print("You are running: "..dt.configuration.running_os) dt.print("You are running: "..dt.configuration.running_os)
-- set the destroy routine so that script_manager can call it when
-- it's time to destroy the script and then return the data to
-- script_manager
local script_data = {}
script_data.destroy = destroy
return script_data
-- --
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

150
examples/x-touch.lua Normal file
View File

@@ -0,0 +1,150 @@
--[[
This file is part of darktable,
copyright (c) 2023 Diederik ter Rahe
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/>.
]]
--[[
X-Touch Mini flexible encoder shortcuts
This script will create virtual sliders that are mapped dynamically to
the most relevant sliders for the currently focused processing module.
Tailored modules are color zones, tone equalizer, color calibration and
mask manager properties. The script can easily be amended for other
devices or personal preferences. Virtual "toggle" buttons can be created
as well, that dynamically change meaning depending on current status.
USAGE
* require this script from your main lua file
* restart darktable
* create shortcuts for each of the encoders on the x-touch mini
to a virtual slider under lua/x-touch
or import the following shortcutsrc file in the shortcuts dialog/preferences tab:
None;midi:CC1=lua/x-touch/knob 1
None;midi:CC2=lua/x-touch/knob 2
None;midi:CC3=lua/x-touch/knob 3
None;midi:CC4=lua/x-touch/knob 4
None;midi:CC5=lua/x-touch/knob 5
None;midi:CC6=lua/x-touch/knob 6
None;midi:CC7=lua/x-touch/knob 7
None;midi:CC8=lua/x-touch/knob 8
midi:E0=global/modifiers
midi:F0=global/modifiers;ctrl
midi:F#0=global/modifiers;alt
midi:G#-1=iop/blend/tools/show and edit mask elements
midi:A-1=iop/colorzones;focus
midi:A#-1=iop/toneequal;focus
midi:B-1=iop/colorbalancergb;focus
midi:C0=iop/channelmixerrgb;focus
]]
local dt = require "darktable"
local du = require "lib/dtutils"
du.check_min_api_version("9.1.0", "x-touch")
-- set up 8 mimic sliders with the same function
for k = 1,8 do
dt.gui.mimic("slider", "knob ".. k,
function(action, element, effect, size)
-- take the number from the mimic name
local k = tonumber(action:sub(-1))
-- only operate in darkroom; return NAN otherwise
if dt.gui.current_view() ~= dt.gui.views.darkroom then
return 0/0
end
local maskval = 0/0
if k < 8 then
-- first try if the mask slider at that position is active
local s = { "opacity",
"size",
"feather",
"hardness",
"rotation",
"curvature",
"compression" }
maskval = dt.gui.action("lib/masks/properties/" .. s[k],
element, effect, size)
end
-- if a value different from NAN is returned, the slider was active
if maskval == maskval then
return maskval
-- try if colorzones module is focused; if so select element of graph
elseif dt.gui.action("iop/colorzones", "focus") ~= 0 then
which = "iop/colorzones/graph"
local e = { "red",
"orange",
"yellow",
"green",
"aqua",
"blue",
"purple",
"magenta" }
element = e[k]
-- if the tone equalizer is focused,
-- select one of the sliders in the "simple" tab
elseif dt.gui.action("iop/toneequal", "focus") ~= 0 then
which ="iop/toneequal/simple/"..(k-9).." EV"
-- if color calibration is focused,
-- the last 4 knobs are sent there
elseif dt.gui.action("iop/channelmixerrgb", "focus") ~= 0
and k >= 5 then
-- knob 5 selects the active tab; pressing it resets to CAT
if k == 5 then
which = "iop/channelmixerrgb/page"
element = "CAT"
-- since the tab action is not a slider,
-- the effects need to be translated
if effect == "up" then effect = "next"
elseif effect == "down" then effect = "previous"
else effect = "activate"
end
else
-- knobs 6, 7 and 8 are for the three color sliders on each tab
which = "iop/focus/sliders"
local e = { "1st",
"2nd",
"3rd" }
element = e[k - 5]
end
-- the 4th knob is contrast;
-- either colorbalance if it is focused, or filmic
elseif dt.gui.action("iop/colorbalancergb", "focus") ~= 0
and k == 4 then
which = "iop/colorbalancergb/contrast"
-- in all other cases use a default selection
else
local s = { "iop/exposure/exposure",
"iop/filmicrgb/white relative exposure",
"iop/filmicrgb/black relative exposure",
"iop/filmicrgb/contrast",
"iop/crop/left",
"iop/crop/right",
"iop/crop/top",
"iop/crop/bottom" }
which = s[k]
end
-- now pass the element/effect/size to the selected slider
return dt.gui.action(which, element, effect, size)
end)
end

View File

@@ -1,61 +0,0 @@
--[[
This file is part of darktable,
copyright (c) 2014 Jérémy Rosen
copyright (c) 2018 Bill Ferguson
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
INCLUDE ALL
Automatically include all scripts in the script repository
This is intended for debugging purpose
USAGE
* require this file from your main lua config file:
* go to configuration => preferences
* Enable the scripts you want to use
* restart darktable
Note that you need to restart DT for your changes to enabled scripts to take effect
]]
local dt = require "darktable"
local io = require "io"
-- must be loaded for scripts using darktable.control_execute to work
require "official/yield"
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
-- find all scripts, but skip the lib and tools directories
local output = io.popen("cd "..dt.configuration.config_dir.."/lua ;find . -name lib -prune -o -name tools -prune -o -name \\*.lua -print")
local my_name={...}
my_name = my_name[1]
for line in output:lines() do
local req_name = line:sub(3,-5)
if req_name ~= my_name and not string.match(req_name, "yield") then
dt.preferences.register(my_name,req_name,"bool","enable "..req_name,
"Should the script "..req_name.." be enabled at next startup",false)
if dt.preferences.read(my_name,req_name,"bool") then
require(req_name)
end
end
end
--
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

View File

@@ -65,10 +65,47 @@ function dtutils.check_min_api_version(min_api, script_name)
dt.print_error("This application is written for lua api version " .. min_api .. " or later.") dt.print_error("This application is written for lua api version " .. min_api .. " or later.")
dt.print_error("The current lua api version is " .. current_api) dt.print_error("The current lua api version is " .. current_api)
dt.print("ERROR: " .. script_name .. " failed to load. Lua API version " .. min_api .. " or later required.") dt.print("ERROR: " .. script_name .. " failed to load. Lua API version " .. min_api .. " or later required.")
dt.control.sleep(2000) -- allow time for the error to display before script_manager writes it's error message
error("Minimum API " .. min_api .. " not met for " .. script_name .. ".", 0) error("Minimum API " .. min_api .. " not met for " .. script_name .. ".", 0)
end end
end end
dtutils.libdoc.functions["check_max_api_version"] = {
Name = [[check_max_api_version]],
Synopsis = [[check the maximum required api version against the current api version]],
Usage = [[local du = require "lib/dtutils"
local result = du.check_max_api_version(max_api, script_name)
max_api - string - the api version that the application was written for (example: "5.0.0")
script_name - string - the name of the script]],
Description = [[check_max_api_version compares the maximum api required for the appllication to
run against the current api version. This function is used when a part of the Lua API that
the script relies on is removed. If the maximum api version is not met, then an
error message is printed saying the script_name failed to load, then an error is thrown causing the
program to stop executing.
Return_Value = [[result - true if the maximum api version is available, false if not.]],
Limitations = [[When using the default handler on a script being executed from the luarc file, the error thrown
will stop the luarc file from executing any remaining statements. This limitation does not apply to script_manger.]],
Example = [[check_max_api_version("9.0.0") does nothing if the api is less than or equal to 9.0.0 otherwise an
error message is printed and an error is thrown stopping execution of the script.]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils.check_max_api_version(max_api, script_name)
local current_api = dt.configuration.api_version_string
if current_api > max_api then
dt.print_error("This application is written for lua api version " .. max_api .. " or earlier.")
dt.print_error("The current lua api version is " .. current_api)
dt.print("ERROR: " .. script_name .. " failed to load. Lua API version " .. max_api .. " or earlier required.")
dt.control.sleep(2000) -- allow time for the error to display before script_manager writes it's error message
error("Maximum API " .. max_api .. " not met for " .. script_name .. ".", 0)
end
end
dtutils.libdoc.functions["split"] = { dtutils.libdoc.functions["split"] = {
Name = [[split]], Name = [[split]],
Synopsis = [[split a string on a specified separator]], Synopsis = [[split a string on a specified separator]],
@@ -184,7 +221,8 @@ function dtutils.prequire(req_name)
if status then if status then
log.msg(log.info, "Loaded " .. req_name) log.msg(log.info, "Loaded " .. req_name)
else else
log.msg(log.info, "Error loading " .. req_name) log.msg(log.error, "Error loading " .. req_name)
log.msg(log.error, "Error returned is " .. lib)
end end
return status, lib return status, lib
end end
@@ -283,4 +321,76 @@ function dtutils.check_os(operating_systems)
return false return false
end end
dtutils.libdoc.functions["find_image_by_id"] = {
Name = [[find_image_by_id]],
Synopsis = [[look up an image by ID in the database]],
Usage = [[local du = require "lib/dtutils"
local img = du.find_image_by_id(imgid)
id - int - the ID to look up
]],
Description = [[find_image_by_id looks up an image by ID in the database.]],
Return_Value = [[result - dt_lua_image_t - image with the given ID if found, nil if not]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils.find_image_by_id(imgid)
if #dt.database == 0 or imgid > dt.database[#dt.database].id then
return nil
end
if dt.configuration.api_version_string >= "6.2.0" then
return dt.database.get_image(imgid)
else
local min = 1
local max = #dt.database
while (max-min)//2 > 0 do
local mid = min + (max-min)//2
local midID = dt.database[mid].id
if imgid == midID then
return dt.database[mid]
elseif imgid < midID then
max = mid-1
else
min = mid+1
end
end
if dt.database[min].id == imgid then
return dt.database[min]
elseif dt.database[max].id == imgid then
return dt.database[max]
else
return nil
end
end
end
dtutils.libdoc.functions["deprecated"] = {
Name = [[deprecated]],
Synopsis = [[print deprecation warning]],
Usage = [[local du = require "lib/dtutils"
du.deprecated(script_name, removal_string)
script_name - name of the script being deprecated
removal_strubg - a string explaining when the script will be removed]],
Description = [[deprecated prints an error message saying the script is deprecated and when it will be removed]],
Return_Value = [[]],
Limitations = [[]],
Example = [[local du = require "lib/dtutils"
du.deprecated("contrib/rename-tags.lua", "darktable release 4.0")]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils.deprecated(script_name, removal_string)
dt.print_toast("WARNING: " .. script_name .. " is deprecated and will be removed in " .. removal_string)
dt.print_error("WARNING: " .. script_name .. " is deprecated and will be removed in " .. removal_string)
end
return dtutils return dtutils

View File

@@ -35,6 +35,316 @@ local function _(msgid)
return gettext.dgettext("dtutils.file", msgid) return gettext.dgettext("dtutils.file", msgid)
end end
--[[
local function to run a test command on windows and return a true if it succeeds
instead of returning true if it runs
]]
local function _win_os_execute(cmd)
local result = nil
local p = io.popen(cmd)
local output = p:read("*a")
p:close()
if string.match(output, "true") then
result = true
else
result = false
end
return result
end
--[[
local function to determine if a path name is a windows executable
]]
local function _is_windows_executable(path)
local result = false
if dtutils_file.test_file(path, "f") then
if string.match(path, ".exe$") or string.match(path, ".EXE$") or
string.match(path, ".com$") or string.match(path, ".COM$") or
string.match(path, ".bat$") or string.match(path, ".BAT$") or
string.match(path, ".cmd$") or string.match(path, ".CMD$") then
result = true
end
end
return result
end
dtutils_file.libdoc.functions["test_file"] = {
Name = [[test_file]],
Synopsis = [[test a file to see what it is]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.test_file(path, test)
path - string - the path to check
test - one of d, e, f, x where
d - directory
e - exists
f - file
x - executable]],
Description = [[test_file checks a path to see if it is a directory]],
Return_Value = [[result - boolean - true if path is a directory, nil if not]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.test_file(path, test)
local cmd = "test -"
local engine = os.execute
local cmdstring = ""
if dt.configuration.running_os == "windows" then
cmd = "if exist "
engine = _win_os_execute
end
if test == "d" then
-- test if directory
if dt.configuration.running_os == "windows" then
cmdstring = cmd .. dtutils_file.sanitize_filename(path .. "\\*") .. " echo true"
else
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
end
elseif test == "e" then
-- test exists
if dt.configuration.running_os == "windows" then
cmdstring = cmd .. dtutils_file.sanitize_filename(path) .. " echo true"
else
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
end
elseif test == "f" then
-- test if file
if dt.configuration.running_os == "windows" then
if not dtutils_file.test_file(path, "d") then -- make sure it's not a directory
cmdstring = cmd .. dtutils_file.sanitize_filename(path) .. " echo true"
else
return false
end
else
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
end
elseif test == "x" then
-- test executable
if dt.configuration.running_os == "windows" then
return _is_windows_executable(path)
else
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
end
else
dt.print_error("[test_file] unknown test " .. test)
return false
end
return engine(cmdstring)
end
--[[
local function to return a case insensitive pattern for matching
i.e. gimp becomes [Gg][Ii][Mm][Pp] which should match any capitalization
of gimp.
]]
local function _case_insensitive_pattern(pattern)
return pattern:gsub("(.)", function(letter)
return string.format("[%s$s]", letter:lower(), letter:upper())
end)
end
--[[
local function to search windows for an executable
]]
local function _search_for_bin_windows(bin)
local result = false
-- use where on path
-- use where on program files
-- use where on program files (x86)
local args = {"", '/R "C:\\Program Files"', '/R "C:\\Program Files (x86)"'}
for _,arg in ipairs(args) do
local cmd = "where " .. arg .. " " .. ds.sanitize(bin)
local p = io.popen(cmd)
local output = p:read("*a")
p:close()
local lines = du.split(output, "\n")
local cibin = _case_insensitive_pattern(bin)
for _,line in ipairs(lines) do
if string.match(line, cibin) then
dt.print_log("found win search match " .. line)
if dtutils_file.test_file(line, "f") and dtutils_file.test_file(line, "x") then
dtutils_file.set_executable_path_preference(bin, line) -- save it so we don't have to search again
return line
end
end
end
end
return result
end
--[[
local function to search *nix systems for an executable
]]
local function _search_for_bin_nix(bin)
local result = false
local p = io.popen("command -v " .. bin)
local output = p:read("*a")
p:close()
if string.len(output) > 0 then
local spath = dtutils_file.sanitize_filename(output:sub(1, -2))
if dtutils_file.test_file(spath, "f") and dtutils_file.test_file(spath, "x") then
dtutils_file.set_executable_path_preference(bin, spath)
result = spath
end
end
return result
end
--[[
local function to search macos systems for an executable
]]
local function _search_for_bin_macos(bin)
local result = false
result = _search_for_bin_nix(bin) -- see if it's in the path
if not result then
local search_start = "/Applications"
if dtutils_file.check_if_file_exists("/Applications/" .. bin .. ".app") then
search_start = "/Applications/" .. bin .. ".app"
end
local p = io.popen("find " .. search_start .. " -type f -name " .. bin .. " -print")
local output = p:read("*a")
p:close()
local lines = du.split(output, "\n")
for _,line in ipairs(lines) do
local spath = dtutils_file.sanitize_filename(line:sub(1, -1))
if dtutils_file.test_file(spath, "x") then
dtutils_file.set_executable_path_preference(bin, spath) -- save it so we don't have to search again
result = spath
end
end
end
return result
end
--[[
local function to provide a generic search call that can be
split into operating system specific calls
]]
local function _search_for_bin(bin)
local result = false
if dt.configuration.running_os == "windows" then
result = _search_for_bin_windows(bin)
if result then
result = dtutils_file.sanitize_filename(result)
end
elseif dt.configuration.running_os == "macos" then
result = _search_for_bin_macos(bin)
else
result = _search_for_bin_nix(bin)
end
return result
end
--[[
local function to check if an executable path is
a windows executable on linux or macos, thus requiring wine to run
]]
local function _check_path_for_wine_bin(path)
local result = false
if string.len(path) > 0 then
-- check for windows executable to run under wine
if _is_windows_executable(path) then
if dtutils_file.check_if_file_exists(path) then
result = "wine " .. dtutils_file.sanitize_filename(path)
end
end
end
return result
end
--[[
local function to check if an executable path is
a valid executable. Some generic checks are done before
system specific checks are done.
]]
local function _check_path_for_bin(bin)
local result = false
local path = nil
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
if string.match(bin, PS) then
path = bin
else
path = dtutils_file.get_executable_path_preference(bin)
-- reset path preference is the returned preference is a directory
if dtutils_file.test_file(path, "d") then
dtutils_file.set_executable_path_preference(bin, "")
path = nil
end
end
if path and dtutils_file.test_file(path, "d") then
path = nil
end
if path and dt.configuration.running_os ~= "windows" then
result = _check_path_for_wine_bin(path)
end
if path and not result then
if dtutils_file.test_file(path, "x") then
result = dtutils_file.sanitize_filename(path)
end
end
return result
end
--[[
local function to the old check_if_bin_exists functionality
on windows in order to decrease the amount of windows being
created and destroyed by system calls.
]]
local function _old_check_if_bin_exists(bin) -- only run on windows if preference checked
local result = false
local path = nil
if string.match(bin, "\\") then
path = bin
else
path = dtutils_file.get_executable_path_preference(bin)
end
if string.len(path) > 0 then
if dtutils_file.check_if_file_exists(path) then
if (string.match(path, ".exe$") or string.match(path, ".EXE$")) then
result = dtutils_file.sanitize_filename(path)
end
end
end
return result
end
dtutils_file.libdoc.functions["check_if_bin_exists"] = { dtutils_file.libdoc.functions["check_if_bin_exists"] = {
Name = [[check_if_bin_exists]], Name = [[check_if_bin_exists]],
Synopsis = [[check if an executable exists]], Synopsis = [[check if an executable exists]],
@@ -44,14 +354,25 @@ dtutils_file.libdoc.functions["check_if_bin_exists"] = {
bin - string - the binary to check for]], bin - string - the binary to check for]],
Description = [[check_if_bin_exists checks to see if the specified binary exists. Description = [[check_if_bin_exists checks to see if the specified binary exists.
check_if_bin_exists first checks to see if a preference for the binary has been check_if_bin_exists first checks to see if a preference for the binary has been
registered and uses that if found. The presence of the file is verified, then registered and uses that if found, after it's verified to be an executable and
quoted and returned. If no preference is specified and the operating system is exist. If no preference exissts, the user's path is checked for the executable.
linux then the which command is used to check for a binary in the path. If found If the executable is not found in the users path, then a search of the operating
that path is returned. If no binary is found, false is returned.]], system is conducted to see if the executable can be found.
If an executalble is found, it's verified to exist and be an executable. Once
the executable is verified, the path is saved as a preference to speed up
subsequent checks. The executable path is sanitized and returned.
If no executable is found, false is returned.]],
Return_Value = [[result - string - the sanitized path of the binary, false if not found]], Return_Value = [[result - string - the sanitized path of the binary, false if not found]],
Limitations = [[]], Limitations = [[If more than one executable that satisfies the search results is found, the
wrong one may be returned. If the wrong value is returned, the user can still specify the
correct execuable using tools/executable_manager. Most packages are well behaved with the
notiable exception being GIMP on windows. Depending on the packager there are multiple
gimp executables, often with version numbers. In this case, the user needs to specify
the location of the correct executable using executable_manager.]],
Example = [[]], Example = [[]],
See_Also = [[]], See_Also = [[executable_manager]],
Reference = [[]], Reference = [[]],
License = [[]], License = [[]],
Copyright = [[]], Copyright = [[]],
@@ -59,152 +380,46 @@ dtutils_file.libdoc.functions["check_if_bin_exists"] = {
function dtutils_file.check_if_bin_exists(bin) function dtutils_file.check_if_bin_exists(bin)
local result = false local result = false
local path = nil
if string.match(bin, "/") or string.match(bin, "\\") then if dt.configuration.running_os == "windows" and dt.preferences.read("dtutils.file", "use_old_check_if_bin_exists", "bool") then
path = bin result = _old_check_if_bin_exists(bin)
else else
path = dtutils_file.get_executable_path_preference(bin)
end
if string.len(path) > 0 then result = _check_path_for_bin(bin)
if dtutils_file.check_if_file_exists(path) then
if (string.match(path, ".exe$") or string.match(path, ".EXE$")) and dt.configuration.running_os ~= "windows" then if not result then
result = dtutils_file.sanitize_filename("wine " .. path) result = _search_for_bin(bin)
else
result = dtutils_file.sanitize_filename(path)
end
end
elseif dt.configuration.running_os == "linux" then
local p = io.popen("which " .. bin)
local output = p:read("*a")
p:close()
if string.len(output) > 0 then
result = dtutils_file.sanitize_filename(output:sub(1,-2))
end end
end end
return result return result
end end
dtutils_file.libdoc.functions["split_filepath"] = { -- the following path, filename, etc functions have
Name = [[split_filepath]], -- moved to the string library since they are string
Synopsis = [[split a filepath into parts]], -- manipulation functions, and to prevent circular
Usage = [[local df = require "lib/dtutils.file" -- library inclusiion.
local result = df.split_filepath(filepath) -- these functions are left here for compatibility
filepath - string - path and filename]], -- with older scripts
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) function dtutils_file.split_filepath(str)
-- strip out single quotes from quoted pathnames return ds.split_filepath(str)
str = string.gsub(str, "'", "")
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, "(.-)(([^\\/]-)%.?([^%.\\/]*))$")
if result["basename"] == "" and result["filetype"]:len() > 1 then
result["basename"] = result["filetype"]
result["filetype"] = ""
end
return result
end 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) function dtutils_file.get_path(str)
local parts = dtutils_file.split_filepath(str) return ds.get_path(str)
return parts["path"]
end 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) function dtutils_file.get_filename(str)
local parts = dtutils_file.split_filepath(str) return ds.get_filename(str)
return parts["filename"]
end 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) function dtutils_file.get_basename(str)
local parts = dtutils_file.split_filepath(str) return ds.get_basename(str)
return parts["basename"]
end 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) function dtutils_file.get_filetype(str)
local parts = dtutils_file.split_filepath(str) return ds.get_filetype(str)
return parts["filetype"]
end end
@@ -230,10 +445,10 @@ function dtutils_file.check_if_file_exists(filepath)
local result = false local result = false
if (dt.configuration.running_os == 'windows') then if (dt.configuration.running_os == 'windows') then
filepath = string.gsub(filepath, '[\\/]+', '\\') filepath = string.gsub(filepath, '[\\/]+', '\\')
local p = io.popen("if exist " .. filepath .. " (echo 'yes') else (echo 'no')") local p = io.popen("if exist " .. dtutils_file.sanitize_filename(filepath) .. " (echo 'yes') else (echo 'no')")
local ans = p:read("*all") local ans = p:read("*all")
p:close() p:close()
if string.match(ans, "yes") then if string.match(ans, "yes") then
result = true result = true
end end
-- result = os.execute('if exist "'..filepath..'" (cmd /c exit 0) else (cmd /c exit 1)') -- result = os.execute('if exist "'..filepath..'" (cmd /c exit 0) else (cmd /c exit 1)')
@@ -383,8 +598,8 @@ dtutils_file.libdoc.functions["filename_increment"] = {
local result = df.filename_increment(filepath) local result = df.filename_increment(filepath)
filepath - string - filename to increment]], filepath - string - filename to increment]],
Description = [[filename_increment solves the problem of filename confllict by adding an Description = [[filename_increment solves the problem of filename confllict by adding an
increment to the filename. If the supplied filename has no increment then 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 "01" is added to the basename. If the filename already has an increment, then
1 is added to it and the filename returned.]], 1 is added to it and the filename returned.]],
Return_Value = [[result - string - the incremented filename]], Return_Value = [[result - string - the incremented filename]],
@@ -436,7 +651,7 @@ dtutils_file.libdoc.functions["create_unique_filename"] = {
filepath - string - the path and filename requested]], filepath - string - the path and filename requested]],
Description = [[create_unique_filename takes a requested filepath and checks to see if 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 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 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.]], increasing until either a unique filename is found or there have been 100 attempts.]],
Return_Value = [[result - string - the incremented filename]], Return_Value = [[result - string - the incremented filename]],
Limitations = [[create_unique_filename will only attempt 100 increments.]], Limitations = [[create_unique_filename will only attempt 100 increments.]],
@@ -515,7 +730,7 @@ dtutils_file.libdoc.functions["executable_path_widget"] = {
local widget = df.executable_path_widget(executables) local widget = df.executable_path_widget(executables)
executables - table - a table of strings that are executable names]], executables - table - a table of strings that are executable names]],
Description = [[executable_path_widget takes a table of executable names Description = [[executable_path_widget takes a table of executable names
and builds a set of file selector widgets to get the path to the executable. and builds a set of file selector widgets to get the path to the executable.
The resulting widgets are wrapped in a box widget and returned.]], The resulting widgets are wrapped in a box widget and returned.]],
Return_Value = [[widget - widget - a widget containing a file selector widget for Return_Value = [[widget - widget - a widget containing a file selector widget for
each executable.]], each executable.]],
@@ -530,10 +745,10 @@ dtutils_file.libdoc.functions["executable_path_widget"] = {
function dtutils_file.executable_path_widget(executables) function dtutils_file.executable_path_widget(executables)
local box_widgets = {} local box_widgets = {}
table.insert(box_widgets, dt.new_widget("section_label"){label = "select executable(s)"}) table.insert(box_widgets, dt.new_widget("section_label"){label = "select executable(s)"})
for _, executable in pairs(executables) do for _, executable in pairs(executables) do
table.insert(box_widgets, dt.new_widget("label"){label = "select " .. executable .. " executable"}) table.insert(box_widgets, dt.new_widget("label"){label = "select " .. executable .. " executable"})
local path = dtutils_file.get_executable_path_preference(executable) local path = dtutils_file.get_executable_path_preference(executable)
if not path then if not path then
path = "" path = ""
end end
table.insert(box_widgets, dt.new_widget("file_chooser_button"){ table.insert(box_widgets, dt.new_widget("file_chooser_button"){
@@ -544,8 +759,9 @@ function dtutils_file.executable_path_widget(executables)
if dtutils_file.check_if_bin_exists(self.value) then if dtutils_file.check_if_bin_exists(self.value) then
dtutils_file.set_executable_path_preference(executable, self.value) dtutils_file.set_executable_path_preference(executable, self.value)
end end
end} end
) }
)
end end
local box = dt.new_widget("box"){ local box = dt.new_widget("box"){
orientation = "vertical", orientation = "vertical",
@@ -562,7 +778,7 @@ dtutils_file.libdoc.functions["sanitize_filename"] = {
local sanitized_filename = df.sanitize_filename(filename) local sanitized_filename = df.sanitize_filename(filename)
filename - string - a filepath and filename]], filename - string - a filepath and filename]],
Description = [[sanitize_file places quotes around the filename in an Description = [[sanitize_file places quotes around the filename in an
operating system specific manner. The result is safe to pass as operating system specific manner. The result is safe to pass as
an argument to the operating system.]], an argument to the operating system.]],
Return_Value = [[sanitized_filename - string - quoted filename]], Return_Value = [[sanitized_filename - string - quoted filename]],
Limitations = [[]], Limitations = [[]],
@@ -584,7 +800,7 @@ dtutils_file.libdoc.functions["mkdir"] = {
df.mkdir(path) df.mkdir(path)
path - string - a directory path]], path - string - a directory path]],
Description = [[mkdir creates directories if not already exists. It Description = [[mkdir creates directories if not already exists. It
create whole parents subtree if needed create whole parents subtree if needed
]], ]],
Return_Value = [[path - string - a directory path]], Return_Value = [[path - string - a directory path]],
@@ -596,7 +812,7 @@ dtutils_file.libdoc.functions["mkdir"] = {
Copyright = [[]], Copyright = [[]],
} }
function dtutils_file.mkdir(path) function dtutils_file.mkdir(path)
if not dtutils_file.check_if_file_exists(path) then if not dtutils_file.check_if_file_exists(path) then
local mkdir_cmd = dt.configuration.running_os == "windows" and "mkdir" or "mkdir -p" local mkdir_cmd = dt.configuration.running_os == "windows" and "mkdir" or "mkdir -p"
return dsys.external_command(mkdir_cmd.." "..path) return dsys.external_command(mkdir_cmd.." "..path)
@@ -627,6 +843,54 @@ function dtutils_file.rmdir(path)
return dsys.external_command(rm_cmd.." "..path) return dsys.external_command(rm_cmd.." "..path)
end end
dtutils_file.libdoc.functions["create_tmp_file"] = {
Name = [[create_tmp_file]],
Synopsis = [[creates a temporary file]],
Usage = [[local df = require "lib/dtutils.file
local result = df.create_tmp_file()]],
Description = [[create_tmp_file can be used to create temporary files]],
Return_Value = [[result - string - path to the created temporary file.]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.create_tmp_file()
local tmp_file = os.tmpname()
if dt.configuration.running_os == "windows" then
tmp_file = dt.configuration.tmp_dir .. tmp_file -- windows os.tmpname() defaults to root directory
end
local f = io.open(tmp_file, "w")
if not f then
log.msg(log.error, string.format("Error writing to `%s`", tmp_file))
os.remove(tmp_file)
return nil
end
return tmp_file
end
--[[
The new check_if_bin_exists() does multiple calls to the operating system to check
if the file exists and is an executable. On windows, each call to the operating system
causes a window to open in order to run the command, then the window closes when the
command exits. If the user gets annoyed by the "flickering windows", then they can
enable this preference to use the old check_if_bin_exists() that relys on the
executable path preferences and doesn't do as many checks.
]]
if dt.configuration.running_os == "windows" then
dt.preferences.register("dtutils.file", "use_old_check_if_bin_exists", "bool",
"lua scripts use old check_if_bin_exists()",
"lessen flickering windows effect when scripts run",
false)
end
return dtutils_file return dtutils_file

View File

@@ -17,11 +17,11 @@ dtutils_log.libdoc = {
print out warning, error and success messages as code is running print out warning, error and success messages as code is running
log.log_level(debug) log.log_level(log.debug)
print out debugging messages too because this isnt working print out debugging messages too because this isnt working
log.log_level(info) log.log_level(log.info)
I want to make sure this is working ok I want to make sure this is working ok
@@ -51,54 +51,63 @@ local dt_print_error = dt.print_error
local dt_print_log = dt.print_log local dt_print_log = dt.print_log
local dt_print = dt.print local dt_print = dt.print
-- set the default log levels -- set the default log levels
dtutils_log.debug = { dtutils_log.debug = {
label = "DEBUG:", label = "DEBUG: ",
enabled = false, enabled = false,
engine = dt_print_log, engine = dt_print_log,
caller_info = 3,
level = 1, level = 1,
} }
dtutils_log.info = { dtutils_log.info = {
label = "INFO:", label = "INFO: ",
enable = false, enable = false,
engine = dt_print_log, engine = dt_print_log,
caller_info = 1,
level = 2, level = 2,
} }
dtutils_log.warn = { dtutils_log.warn = {
label = "WARN:", label = "WARN: ",
enabled = false, enabled = false,
engine = dt_print_log, engine = dt_print_log,
caller_info = 2,
level = 3, level = 3,
} }
dtutils_log.error = { dtutils_log.error = {
label = "ERROR:", label = "ERROR: ",
enabled = true, enabled = true,
engine = dt_print_error, engine = dt_print_log,
caller_info = 3,
level = 4, level = 4,
} }
dtutils_log.success = { dtutils_log.success = {
label = "SUCCESS:", label = "SUCCESS: ",
enabled = true, enabled = true,
engine = dt_print_log, engine = dt_print_log,
caller_info = 2,
level = 5, level = 5,
} }
dtutils_log.screen = { dtutils_log.screen = {
label = "", label = "",
enabled = true, enabled = true,
engine = dt_print, engine = dt_print,
caller_info = 0,
level = 9, level = 9,
} }
dtutils_log.always = { dtutils_log.always = {
label = "", label = "",
enabled = true, enabled = true,
engine = dt_print_log, engine = dt_print_log,
caller_info = 3,
level = 9, level = 9,
} }
dtutils_log.critical = { dtutils_log.critical = {
label = "CRITICAL:", label = "CRITICAL: ",
enabled = true, enabled = true,
engine = print, engine = print,
caller_info = 3,
level = 9, level = 9,
} }
@@ -120,7 +129,7 @@ result = log.caller(level)
Copyright = [[]], Copyright = [[]],
} }
function dtutils_log.caller(level) function dtutils_log.caller(level, info)
local name = debug.getinfo(level).name local name = debug.getinfo(level).name
local lineno = nil local lineno = nil
local source = nil local source = nil
@@ -131,7 +140,16 @@ function dtutils_log.caller(level)
-- we just need the filename, so grab it from the string -- we just need the filename, so grab it from the string
-- Thanks, Tobias Jakobs :-) -- Thanks, Tobias Jakobs :-)
source = string.match(source, "@.-([^\\/]-%.?[^%.\\/]*)$") source = string.match(source, "@.-([^\\/]-%.?[^%.\\/]*)$")
return name .. ": " .. source .. ": " .. lineno .. ":"
if info == 0 then
return ""
elseif info == 1 then
return source .. ": "
elseif info == 2 then
return source .. ": " .. name .. ": "
else
return source .. ": " .. name .. ": " .. lineno .. ":"
end
else else
return "callback:" return "callback:"
end end
@@ -182,7 +200,7 @@ function dtutils_log.msg(level, ...)
end end
local log_msg = level.label local log_msg = level.label
if level.engine ~= dt_screen and call_level ~= 0 then if level.engine ~= dt_screen and call_level ~= 0 then
log_msg = log_msg .. dtutils_log.caller(call_level) .. " " log_msg = log_msg .. dtutils_log.caller(call_level, level.caller_info) .. " "
elseif log_msg:len() > 2 then elseif log_msg:len() > 2 then
log_msg = log_msg .. " " log_msg = log_msg .. " "
end end

View File

@@ -174,6 +174,59 @@ function dtutils_string.urlencode(str)
end end
dtutils_string.libdoc.functions["is_not_sanitized"] = {
Name = [[is_not_sanitized]],
Synopsis = [[Check if a string has been sanitized]],
Usage = [[local ds = require "lib/dtutils.string"
local result = ds.is_not_sanitized(str)
str - string - the string that needs to be made safe]],
Description = [[is_not_sanitized checks a string to see if it
has been made safe use passing as an argument in a system command.]],
Return_Value = [[result - boolean - true if the string is not sanitized otherwise false]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
local function _is_not_sanitized_posix(str)
-- A sanitized string must be quoted.
if not string.match(str, "^'.*'$") then
return true
-- A quoted string containing no quote characters within is sanitized.
elseif string.match(str, "^'[^']*'$") then
return false
end
-- Any quote characters within a sanitized string must be properly
-- escaped.
local quotesStripped = string.sub(str, 2, -2)
local escapedQuotesRemoved = string.gsub(quotesStripped, "'\\''", "")
if string.find(escapedQuotesRemoved, "'") then
return true
else
return false
end
end
local function _is_not_sanitized_windows(str)
if not string.match(str, "^\".*\"$") then
return true
else
return false
end
end
function dtutils_string.is_not_sanitized(str)
if dt.configuration.running_os == "windows" then
return _is_not_sanitized_windows(str)
else
return _is_not_sanitized_posix(str)
end
end
dtutils_string.libdoc.functions["sanitize"] = { dtutils_string.libdoc.functions["sanitize"] = {
Name = [[sanitize]], Name = [[sanitize]],
Synopsis = [[surround a string in quotes making it safe to pass as an argument]], Synopsis = [[surround a string in quotes making it safe to pass as an argument]],
@@ -192,26 +245,40 @@ dtutils_string.libdoc.functions["sanitize"] = {
Copyright = [[]], Copyright = [[]],
} }
function dtutils_string.sanitize(str) local function _sanitize_posix(str)
local result = "" if _is_not_sanitized_posix(str) then
local os_quote = dt.configuration.running_os == "windows" and '"' or "'" return "'" .. string.gsub(str, "'", "'\\''") .. "'"
else
if dtutils_string.is_not_sanitized(str) then return str
result = os_quote .. str .. os_quote
end end
return result
end end
dtutils_string.libdoc.functions["is_not_sanitized"] = { local function _sanitize_windows(str)
Name = [[is_not_sanitized]], if _is_not_sanitized_windows(str) then
Synopsis = [[Check if a string has been sanitized]], return "\"" .. string.gsub(str, "\"", "\"^\"\"") .. "\""
else
return str
end
end
function dtutils_string.sanitize(str)
if dt.configuration.running_os == "windows" then
return _sanitize_windows(str)
else
return _sanitize_posix(str)
end
end
dtutils_string.libdoc.functions["sanitize_lua"] = {
Name = [[sanitize_lua]],
Synopsis = [[escape lua 'magic' characters from a pattern string]],
Usage = [[local ds = require "lib/dtutils.string" Usage = [[local ds = require "lib/dtutils.string"
local result = ds.is_not_sanitized(str)
local result = ds.sanitize_lua(str)
str - string - the string that needs to be made safe]], str - string - the string that needs to be made safe]],
Description = [[is_not_sanitized checks a string to see if it Description = [[sanitize_lua escapes lua 'magic' characters so that
has been made safe use passing as an argument in a system command.]], a string may be used in lua string/patten matching.]],
Return_Value = [[result - boolean - true if the string is not sanitized otherwise false]], Return_Value = [[result - string - a lua pattern safe string]],
Limitations = [[]], Limitations = [[]],
Example = [[]], Example = [[]],
See_Also = [[]], See_Also = [[]],
@@ -220,15 +287,134 @@ dtutils_string.libdoc.functions["is_not_sanitized"] = {
Copyright = [[]], Copyright = [[]],
} }
function dtutils_string.is_not_sanitized(str) function dtutils_string.sanitize_lua(str)
local os_quote = dt.configuration.running_os == "windows" and '"' or "'" str = string.gsub(str, "%-", "%%-")
str = string.gsub(str, "%(", "%%(")
str = string.gsub(str, "%)", "%%)")
return str
end
if string.match(str, os_quote .. ".*" .. os_quote) then dtutils_string.libdoc.functions["split_filepath"] = {
return false Name = [[split_filepath]],
else Synopsis = [[split a filepath into parts]],
return true Usage = [[local ds = require "lib/dtutils.string"
local result = ds.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_string.split_filepath(str)
-- strip out single quotes from quoted pathnames
str = string.gsub(str, "'", "")
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, "(.-)(([^\\/]-)%.?([^%.\\/]*))$")
if result["basename"] == "" and result["filetype"]:len() > 1 then
result["basename"] = result["filetype"]
result["filetype"] = ""
end end
return result
end
dtutils_string.libdoc.functions["get_path"] = {
Name = [[get_path]],
Synopsis = [[get the path from a file path]],
Usage = [[local ds = require "lib/dtutils.string"
local result = ds.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_string.get_path(str)
local parts = dtutils_string.split_filepath(str)
return parts["path"]
end
dtutils_string.libdoc.functions["get_filename"] = {
Name = [[get_filename]],
Synopsis = [[get the filename and extension from a file path]],
Usage = [[local ds = require "lib/dtutils.string"
local result = ds.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_string.get_filename(str)
local parts = dtutils_string.split_filepath(str)
return parts["filename"]
end
dtutils_string.libdoc.functions["get_basename"] = {
Name = [[get_basename]],
Synopsis = [[get the filename without the path or extension]],
Usage = [[local ds = require "lib/dtutils.string"
local result = ds.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_string.get_basename(str)
local parts = dtutils_string.split_filepath(str)
return parts["basename"]
end
dtutils_string.libdoc.functions["get_filetype"] = {
Name = [[get_filetype]],
Synopsis = [[get the filetype from a filename]],
Usage = [[local ds = require "lib/dtutils.string"
local result = ds.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_string.get_filetype(str)
local parts = dtutils_string.split_filepath(str)
return parts["filetype"]
end end
return dtutils_string return dtutils_string

View File

@@ -1,7 +1,6 @@
local dtutils_system = {} local dtutils_system = {}
local dt = require "darktable" local dt = require "darktable"
require "official/yield" -- necessary for dt.control.execute()
dtutils_system.libdoc = { dtutils_system.libdoc = {
Name = [[dtutils.system]], Name = [[dtutils.system]],

View File

@@ -0,0 +1,78 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Volker Lenhardt <volker.lenhardt@unitybox.de>, 2020.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-06-11 19:07+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: de_DE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: OpenInExplorer.lua:71
msgid ""
"OpenInExplorer plug-in only supports Linux and macOS, and Windows at this time"
msgstr "OpenInExplorer-Plug-in unterstützt zur Zeit nur Linux, macOS oder Windows"
#: OpenInExplorer.lua:87
msgid ""
"No links directory selected.\n"
"Please check the dt preferences (lua options)"
msgstr "Kein Verknüpfungs-Verzeichnis ausgewählt.\nBitte die dt-Voreinstellungen überprüfen (Lua-Optionen)"
#: OpenInExplorer.lua:91
#, lua-format
msgid ""
"Links directory '%s' not found.\n"
"Please check the dt preferences (lua options)"
msgstr "Das Verknüpfungs-Verzeichnis '%s' konnte nicht gefunden werden.\nBitte die dt-Voreinstellungen überprüfen (Lua-Optionen)"
#: OpenInExplorer.lua:148
msgid "Failed to create links. Missing rights?"
msgstr "Die Verknüpfungen konnten nicht erstellt werden. Fehlende Rechte?"
#: OpenInExplorer.lua:164
msgid "Please select an image"
msgstr "Es wurde kein Bild ausgewählt"
#: OpenInExplorer.lua:170
msgid "Please select fewer images (max. 15)"
msgstr "Bitte nicht mehr als 15 Bilder auswählen"
#: OpenInExplorer.lua:186
msgid "show in file explorer"
msgstr "im Dateimanager anzeigen"
#: OpenInExplorer.lua:188
msgid "Open the file manager at the selected image's location"
msgstr "Öffnet den Dateimanager an der Position des ausgewählten Bildes"
#: OpenInExplorer.lua:193
msgid "OpenInExplorer: linked files directory"
msgstr "OpenInExplorer: Verknüpfungs-Verzeichnis"
#: OpenInExplorer.lua:194
msgid ""
"Directory to store the links to the file names. Requires restart to take "
"effect"
msgstr "Verzeichnis für die Verknüpfungen zu den Dateinamen. Erfordert dt-Neustart"
#: OpenInExplorer.lua:197
msgid "Select directory"
msgstr "Verzeichnis auswählen"
#: OpenInExplorer.lua:203
msgid "OpenInExplorer: use links"
msgstr "OpenInExplorer: Verknüpfungen nutzen"
#: OpenInExplorer.lua:204
msgid "Use links instead of multiple windows. Requires restart to take effect"
msgstr "Verknüpfungen statt einzelner Fenster nutzen. Erfordert dt-Neustart"

View File

@@ -0,0 +1,73 @@
# 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: Lua photils\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-12 13:12+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: \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"
msgid "get tags"
msgstr "hole stichwörter"
msgid "requires a restart to be applied"
msgstr "erfordert einen Neustart um angewendet zu werden"
msgid "min confidence value"
msgstr "min vertrauenswert"
msgid ""
"The suggested tags were not generated\n"
" for the currently selected image!"
msgstr ""
"Die vorgeschlagenen Stichwörter wurden für das\n"
"aktuell ausgewählte Bild nicht generiert!"
#, lua-format
msgid " page %s of %s "
msgstr " seite %s von %s "
msgid "Tags successfully attached to image"
msgstr "Stichwörter erfolgreich an Bild angefügt"
#, lua-format
msgid "Error writing to `%s`"
msgstr "Fehler beim Schreiben in `%s`"
#, lua-format
msgid "%s failed, see terminal output for details"
msgstr "%s fehlgeschlagen, siehe Terminal-Ausgabe für Details"
#, lua-format
msgid "%s found %d tags for your image"
msgstr "%s gefunden %d-Stichwörter für Ihr Bild"
msgid "No image selected."
msgstr "Kein Bild ausgewählt."
msgid "This plugin can only handle a single image."
msgstr "Dieses Plugin kann nur ein einziges Bild verarbeiten."
#, lua-format
msgid "attach %d tags"
msgstr "%d stichwörter anhängen"
msgid "photils-cli not found"
msgstr "photils-cli nicht gefunden"
msgid ""
"Select an image, click \"Get Tags\" and get \n"
"suggestions for tags."
msgstr ""
"Wählen Sie ein Bild aus, drücken Sie \"Hole Stichwörter\"\n"
" und erhalten Sie Vorschläge für Stichwörter."

View File

@@ -0,0 +1,12 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
msgid "select non existing images"
msgstr "nicht vorhandene Bilder auswählen"
msgid "select non existing"
msgstr "nicht vorhandene auswählen"
msgid "select all non-existing images in the current images"
msgstr "alle nicht vorhandenen Bilder in den aktuellen Bildern auswählen"

View File

@@ -0,0 +1,85 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2022-06-27 17:22+0200\n"
"PO-Revision-Date: 2022-06-27 17:36+0200\n"
"Last-Translator: Manuel PINTOR <manu@lepassepartout.fr>\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 2.3\n"
"X-Poedit-Basepath: ../../../contrib\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Poedit-SearchPath-0: OpenInExplorer.lua\n"
#: OpenInExplorer.lua:79
msgid ""
"OpenInExplorer plug-in only supports Linux, macOS, and Windows at this time"
msgstr ""
"Le script OpenInExplorer ne fonctionne que sur Linux, macOS et Windows pour "
"le moment"
#: OpenInExplorer.lua:95
msgid ""
"No links directory selected.\n"
"Please check the dt preferences (lua options)"
msgstr ""
"Aucun répertoire de liens sélectionné.\n"
"Voir dans les préférences dt > options lua"
#: OpenInExplorer.lua:99
#, lua-format
msgid ""
"Links directory '%s' not found.\n"
"Please check the dt preferences (lua options)"
msgstr ""
"Répertoire de liens '%s' inexistant.\n"
"Voir dans les préférences dt > options lua"
#: OpenInExplorer.lua:156
msgid "Failed to create links. Missing rights?"
msgstr "Erreur de création de liens. Droits insuffisants ?"
#: OpenInExplorer.lua:172
msgid "Please select an image"
msgstr "Sélectionner une image SVP"
#: OpenInExplorer.lua:178
msgid "Please select fewer images (max. 15)"
msgstr "Sélectionner moins d'images SVP (max. 15)"
#: OpenInExplorer.lua:203
msgid "show in file explorer"
msgstr "afficher dans le gestionnaire de fichiers"
#: OpenInExplorer.lua:205
msgid "Open the file manager at the selected image's location"
msgstr "Ouvre le gestionnaire de fichiers à l'endroit de l'image sélectionnée"
#: OpenInExplorer.lua:212
msgid "OpenInExplorer: linked files directory"
msgstr "OpenInExplorer : répertoire des fichiers liés"
#: OpenInExplorer.lua:213
msgid ""
"Directory to store the links to the file names. Requires restart to take "
"effect"
msgstr ""
"Répertoire dans lequel enregistrer les liens sur les fichiers. Nécessite un "
"redémarrage de dt"
#: OpenInExplorer.lua:216
msgid "Select directory"
msgstr "Sélectionner un répertoire"
#: OpenInExplorer.lua:222
msgid "OpenInExplorer: use links"
msgstr "OpenInExplorer : utiliser des liens"
#: OpenInExplorer.lua:223
msgid "Use links instead of multiple windows. Requires restart to take effect"
msgstr ""
"Utiliser des liens plutôt que de multiples fenêtres. Nécessite un "
"redémarrage de dt"

View File

@@ -7,21 +7,29 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-09-07 22:25-0400\n" "POT-Creation-Date: 2020-12-27 17:55+0100\n"
"PO-Revision-Date: 2016-10-03 14:55+0200\n" "PO-Revision-Date: 2020-12-27 17:55+0100\n"
"Last-Translator: Pascal Obry <pascal@obry.net>\n" "Last-Translator: Christophe Agathon <christophe.agathon@gmail.com>\n"
"Language-Team: \n"
"Language: fr_FR\n" "Language: fr_FR\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.9\n" "X-Generator: Poedit 2.3\n"
"Language: fr_FR\n"
"X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: gettext;dgettext:2;dcgettext:2ngettext:1,2;" "X-Poedit-KeywordsList: _;gettext;dgettext:2;dcgettext:2ngettext:1,2;"
"dngettext:2,3\n" "dngettext:2,3\n"
"X-Poedit-Basepath: .\n" "X-Poedit-Basepath: ../../../contrib\n"
"Language-Team: \n" "X-Poedit-SearchPath-0: clear_GPS.lua\n"
#: clear_GPS.lua:62 #: clear_GPS.lua:67
msgid "clear GPS data" msgid "clear GPS data"
msgstr "effacer données GPS" msgstr "effacer données GPS"
#: clear_GPS.lua:69
msgid "Clear GPS data from selected images"
msgstr "Effacer les données GPS des images sélectionnées"
#: clear_GPS.lua:75
msgid "Clear GPS data"
msgstr "Effacer les données GPS"

View File

@@ -0,0 +1,78 @@
# French messages for copy_attach_detach_tags.lua
# Christophe Agathon <christophe.agathon@gmail.com>, 2021
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2021-01-10 19:39+0100\n"
"PO-Revision-Date: 2021-01-10 19:43+0100\n"
"Last-Translator: Christophe Agathon <christophe.agathon@gmail.com>\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 2.3\n"
"X-Poedit-Basepath: ../../../contrib\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Poedit-KeywordsList: _\n"
"X-Poedit-SearchPath-0: copy_attach_detach_tags.lua\n"
#: copy_attach_detach_tags.lua:93
msgid "Image tags copied ..."
msgstr "Mots-clés de l'image copiés ..."
#: copy_attach_detach_tags.lua:115
msgid "No tag to attach, please copy tags first."
msgstr "Pas de mot-clé à attacher, veuillez d'abord copier les mots-clés."
#: copy_attach_detach_tags.lua:139
msgid "Tags attached ..."
msgstr "Mots-clés attachés ..."
#: copy_attach_detach_tags.lua:155
msgid "Tags removed from image(s)."
msgstr "Mots-clés supprimés des images."
#: copy_attach_detach_tags.lua:161
msgid "Tags replaced"
msgstr "Mots-clés remplacés"
#: copy_attach_detach_tags.lua:166
msgid "tagging addon"
msgstr "mots-clés extra"
#: copy_attach_detach_tags.lua:189
msgid "tag clipboard"
msgstr "presse-papier des mots-clés"
#: copy_attach_detach_tags.lua:197
msgid "multi copy tags"
msgstr "copier mots-clés"
#: copy_attach_detach_tags.lua:198 copy_attach_detach_tags.lua:252
msgid "copy tags from selected image(s)"
msgstr "copier les mots-clés des images sélectionnnées"
#: copy_attach_detach_tags.lua:201 copy_attach_detach_tags.lua:257
msgid "paste tags to selected image(s)"
msgstr "coller les mots clés dans les images sélectionnées"
#: copy_attach_detach_tags.lua:202
msgid "paste tags"
msgstr "coller mots-clés"
#: copy_attach_detach_tags.lua:210
msgid "replace tags"
msgstr "remplacer mots-clés"
#: copy_attach_detach_tags.lua:211 copy_attach_detach_tags.lua:267
msgid "replace tags from selected image(s)"
msgstr "remplacer les mots-clés des images sélectionnées"
#: copy_attach_detach_tags.lua:214
msgid "remove all tags"
msgstr "supprimer tous mots-clés"
#: copy_attach_detach_tags.lua:215 copy_attach_detach_tags.lua:262
msgid "remove tags from selected image(s)"
msgstr "supprimer les mots-clés des images sélectionnées"

View File

@@ -0,0 +1,35 @@
# French messages for copy_paste_metadata.lua
# Christophe Agathon <christophe.agathon@gmail.com>, 2020
msgid ""
msgstr ""
"Project-Id-Version: copy_paste_metadata\n"
"POT-Creation-Date: 2020-12-26 12:49+0100\n"
"PO-Revision-Date: 2020-12-26 12:52+0100\n"
"Last-Translator: Christophe Agathon <christophe.agathon@gmail.com>\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 2.3\n"
"X-Poedit-Basepath: ../../../official\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: _\n"
"X-Poedit-SearchPath-0: copy_paste_metadata.lua\n"
#: copy_paste_metadata.lua:115
msgid "copy metadata"
msgstr "copier les métadonnées"
#: copy_paste_metadata.lua:117
msgid "copy metadata of the first selected image"
msgstr "copier les métadonnées de la première image sélectionnée"
#: copy_paste_metadata.lua:121
msgid "paste metadata"
msgstr "coller les métadonnées"
#: copy_paste_metadata.lua:123
msgid "paste metadata to the selected images"
msgstr "coller les métadonnées vers les images sélectionnées"

View File

@@ -0,0 +1,117 @@
# French messages for photils.lua
# Christophe Agathon <christophe.agathon@gmail.com>, 2021
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2021-08-13 09:04+0200\n"
"PO-Revision-Date: 2021-08-13 09:49+0200\n"
"Last-Translator: Christophe Agathon <christophe.agathon@gmail.com>\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 2.4.1\n"
"X-Poedit-Basepath: ../../../contrib\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Poedit-SearchPath-0: photils.lua\n"
#: photils.lua:117
msgid "get tags"
msgstr "trouver des mots-clés"
#: photils.lua:148
msgid "requires a restart to be applied"
msgstr "nécessite un redémarrage pour être pris en compte"
#: photils.lua:163
msgid "min confidence value"
msgstr "niveau de confiance minimum"
#: photils.lua:180
msgid ""
"The suggested tags were not generated\n"
" for the currently selected image!"
msgstr ""
"La proposition de mots-clés n'a pas été générée\n"
" pour l'image sélectionnée actuellement !"
#: photils.lua:186
#, lua-format
msgid " page %s of %s "
msgstr " page %s de %s "
#: photils.lua:237
msgid "Apply tag to image"
msgstr "Appliquer les mots-clés à l'image"
#: photils.lua:249
msgid "Tags successfully attached to image"
msgstr "Les mots-clés ont bien été ajoutés à l'image"
#: photils.lua:299
#, lua-format
msgid "%s found %d tags for your image"
msgstr "%s a trouvé %d mots-clés pour votre image"
#: photils.lua:315
msgid "No image selected."
msgstr "Pas d'image sélectionnée."
#: photils.lua:319
msgid "This plugin can only handle a single image."
msgstr "Ce plugin ne peut gérer qu'une seule image."
#: photils.lua:326
#, lua-format
msgid "%s failed, see terminal output for details"
msgstr ""
"%s a échoué, consultez les messages sur le terminal pour plus de détails"
#: photils.lua:334
#, lua-format
msgid "no tags where found"
msgstr "aucun mot-clé n'a été trouvé"
#: photils.lua:365
#, lua-format
msgid "attach %d tags"
msgstr "ajouter %d mots-clés"
#: photils.lua:429 photils.lua:430
msgid "photils-cli not found"
msgstr "photils-cli non trouvé"
#: photils.lua:432
msgid ""
"Select an image, click \"get tags\" and get \n"
"suggestions for tags."
msgstr ""
"Sélectionnez une image, cliquez sur « trouver des mots-clés » \n"
"pour obtenir une proposition de mots-clés."
#: photils.lua:467
msgid "photils: show confidence value"
msgstr "photils: afficher le niveau de confiance"
# Original message should begin with capital I
#: photils.lua:468
msgid "if enabled, the confidence value for each tag is displayed"
msgstr "Si activé, le niveau de confiance de chaque mot-clé est affiché"
#: photils.lua:474
msgid "photils: use exported image for tag request"
msgstr "photils: utiliser l'image exportée pour la recherche de mots-clés"
#: photils.lua:475
msgid ""
"If enabled, the image passed to photils for tag suggestion is based on the "
"exported, already edited image. Otherwise, the embedded thumbnail of the RAW "
"file will be used for tag suggestion.The embedded thumbnail could speedup "
"the tag suggestion but can fail if the RAW file is not supported."
msgstr ""
"Si activé, l'image passée à photils pour la proposition de mots-clés est "
"basée sur l'export de l'image déjà éditée. Sinon, c'est la vignette "
"encapsulée dans le fichier RAW qui sera utilisée. La vignette encapsulée "
"pourra accélérer la proposition des mots-clés mais la recherche échouera si "
"le fichier RAW n'est pas supporté."

View File

@@ -0,0 +1,188 @@
# French messages for script_manager.lua
# Christophe Agathon <christophe.agathon@gmail.com>, 2020
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2020-12-28 10:22+0100\n"
"PO-Revision-Date: 2020-12-28 10:24+0100\n"
"Last-Translator: Christophe Agathon <christophe.agathon@gmail.com>\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 2.3\n"
"X-Poedit-Basepath: ../../../tools\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Poedit-KeywordsList: _\n"
"X-Poedit-SearchPath-0: script_manager.lua\n"
#: script_manager.lua:190
msgid "Cant read from "
msgstr "Ne peut pas lire "
#: script_manager.lua:204
msgid "Loaded "
msgstr "Script chargé : "
#: script_manager.lua:207
msgid " failed to load"
msgstr " non chargé"
#: script_manager.lua:223
msgid " will not start when darktable is restarted"
msgstr " ne démarrera pas lorsque darktable sera redémarré"
#: script_manager.lua:281 script_manager.lua:345
msgid "find command is "
msgstr "la commande find est "
#: script_manager.lua:321
msgid "lua scripts successfully updated"
msgstr "mise à jour des scripts lua réussie"
#: script_manager.lua:381
msgid "category "
msgstr "la catégorie "
#: script_manager.lua:381
msgid " is already in use. Please specify a different category name."
msgstr " est déjà utilisée. Indiquez un autre nom de catégorie SVP."
#: script_manager.lua:410
msgid "scripts successfully installed into category "
msgstr "les scripts ont été installés dans la catégorie "
#: script_manager.lua:426
msgid "No scripts found to install"
msgstr "Nous n'avons pas trouvé de script à installer"
#: script_manager.lua:430
msgid "failed to download scripts"
msgstr "impossible de télécharger les scripts"
# Peut-être que « activé » serait mieux, mais ce n'est pas la traduction de « started »
#: script_manager.lua:458
msgid " started"
msgstr " démarré"
# Peut-être que « désactivé » serait mieux, mais ce n'est pas la traduction de « stopped »
#: script_manager.lua:460
msgid " stopped"
msgstr " arrêté"
#: script_manager.lua:545
msgid "Page "
msgstr "Page "
#: script_manager.lua:545
msgid " of "
msgstr " sur "
#: script_manager.lua:674
msgid "scripts to update"
msgstr "scripts à mettre à jour"
#: script_manager.lua:675
msgid "select the scripts installation to update"
msgstr "sélectionnez les scripts à mettre à jour"
#: script_manager.lua:684 script_manager.lua:742
msgid "update scripts"
msgstr "mettre à jour les scripts"
#: script_manager.lua:685
msgid "update the lua scripts from the repository"
msgstr "metre à jour les scripts depuis le dépôt"
#: script_manager.lua:696
msgid "enter the URL of the git repository containing the scripts you wish to add"
msgstr "indiquez l'URL du dépôt git contenant les scripts que vous souhaitez ajouter"
#: script_manager.lua:701
msgid "name of new category"
msgstr "nom de la nouvelle catégorie"
#: script_manager.lua:702
msgid "enter a category name for the additional scripts"
msgstr "indiquez un nom de catégorie pour les scripts supplémentaires"
#: script_manager.lua:707
msgid "URL to download additional scripts from"
msgstr "URL d'où seront téléchargés les scripts"
#: script_manager.lua:709
msgid "new category to place scripts in"
msgstr "nouvelle catégorie où seront placés les scripts"
#: script_manager.lua:712
msgid "install additional scripts"
msgstr "installer les scripts supplémentaires"
#: script_manager.lua:720
msgid "Enable \"Disable Scripts\" button"
msgstr "Activer le bouton « Désactiver les Scripts »"
#: script_manager.lua:730
msgid "Disable Scripts"
msgstr "Désactiver les scripts"
#: script_manager.lua:736
msgid "lua scripts will not run the next time darktable is started"
msgstr "les scripts lua ne seront pas lancés au prochain démarrage de darktable"
#: script_manager.lua:745
msgid "add more scripts"
msgstr "ajouter des scripts"
#: script_manager.lua:747
msgid "disable scripts"
msgstr "désactiver les scripts"
#: script_manager.lua:755
msgid "category"
msgstr "catégorie"
#: script_manager.lua:756
msgid "select the script category"
msgstr "sélectionner la catégorie"
#: script_manager.lua:780
msgid "Page:"
msgstr "Page :"
#: script_manager.lua:808
msgid "Scripts"
msgstr "Scripts"
#: script_manager.lua:817
msgid "scripts per page"
msgstr "scripts par page"
#: script_manager.lua:818
msgid "select number of start/stop buttons to display"
msgstr "sélectionner le nombre de boutons marche/arrêt à afficher"
#: script_manager.lua:829
msgid "change number of buttons"
msgstr "changer le nombre de boutons"
#: script_manager.lua:837
msgid "Configuration"
msgstr "Configuration"
#: script_manager.lua:858
msgid "action"
msgstr "action"
#: script_manager.lua:864
msgid "install/update scripts"
msgstr "installer/mettre à jour des scripts"
#: script_manager.lua:864
msgid "configure"
msgstr "configurer"
#: script_manager.lua:864
msgid "start/stop scripts"
msgstr "démarrer/arrêter des scripts"

View File

@@ -0,0 +1,62 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Maurizio Paglia <mpaglia0@gmail.com>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-07-11 22:19+0200\n"
"PO-Revision-Date: 2022-07-11 22:21+0200\n"
"Last-Translator: Maurizio Paglia <mpaglia0@gmail.com>\n"
"Language-Team: Italian <>\n"
"Language: it\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 21.12.3\n"
#: executable_manager.lua:144
msgid "No executable paths found, exiting..."
msgstr "Nessun percorso con eseguibili, esco..."
#: executable_manager.lua:160
msgid "select an executable"
msgstr "seleziona un eseguibile"
#: executable_manager.lua:160
msgid "search path for executable"
msgstr "cerca il percorso dell'eseguibile"
#: executable_manager.lua:169
msgid "select "
msgstr "seleziona"
#: executable_manager.lua:169
msgid " executable"
msgstr " eseguibile"
#: executable_manager.lua:190
msgid "select executable to modify"
msgstr "Seleziona l'eseguibile da modificare"
#: executable_manager.lua:205
msgid "current"
msgstr "Attuale"
#: executable_manager.lua:207
msgid "select"
msgstr "Seleziona"
#: executable_manager.lua:209
msgid "reset"
msgstr "Cancella"
#: executable_manager.lua:212
msgid "Clear path for "
msgstr "Cancella il percorso di "
#: executable_manager.lua:211
msgid "clear"
msgstr "Pulisci"

View File

@@ -0,0 +1,122 @@
# Italian translations for ext_editor package.
# Copyright (C) 2019 THE ext_editor'S COPYRIGHT HOLDER
# This file is distributed under the same license as the ext_editor package.
# marco <marco.carrarini@gmail.com>, 2019.
#
msgid ""
msgstr ""
"Project-Id-Version: ext_editor\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-01-01 19:15+0100\n"
"PO-Revision-Date: 2019-12-28 21:45+0100\n"
"Last-Translator: marco <marco.carrarini@gmail.com>\n"
"Language-Team: Italian\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ASCII\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ext_editor.lua:143
msgid " editors configured"
msgstr " editor configurati"
#: ext_editor.lua:150
msgid "exporting image "
msgstr "Esportazione immagine "
#: ext_editor.lua:159
msgid "not a valid choice"
msgstr "Scelta non valida"
#: ext_editor.lua:165
msgid "please select one image"
msgstr "Selezionare una immagine"
#: ext_editor.lua:174
msgid " not found"
msgstr " non trovato"
#: ext_editor.lua:195
msgid "file type not allowed"
msgstr "tipo di file non consentito"
#: ext_editor.lua:232
msgid "error copying file "
msgstr "Errore copia file "
#: ext_editor.lua:239
msgid "launching "
msgstr "Lancio "
#: ext_editor.lua:242
msgid "error launching "
msgstr "Errore lancio "
#: ext_editor.lua:341
msgid "error moving file "
msgstr "Errore spostamento file "
#: ext_editor.lua:355
msgid "collection"
msgstr "Collezione"
#: ext_editor.lua:360
msgid "choose program"
msgstr "Scegli programma"
#: ext_editor.lua:361
msgid "select the external editor from the list"
msgstr "Seleziona un editor esterno dalla lista"
#: ext_editor.lua:371
msgid "edit"
msgstr "Modifica"
#: ext_editor.lua:372
msgid "open the selected image in external editor"
msgstr "Apre immagine selezionata in editor esterno"
#: ext_editor.lua:382
msgid "edit a copy"
msgstr "Modifica copia"
#: ext_editor.lua:383
msgid "create a copy of the selected image and open it in external editor"
msgstr "Apre una copia della immagine selezionata in editor esterno"
#: ext_editor.lua:392
msgid "update list"
msgstr "Aggiorna lista"
#: ext_editor.lua:393
msgid "update list of programs if lua preferences are changed"
msgstr "Aggiorna la lista dei programmi se le preferenze di lua sono cambiate"
#: ext_editor.lua:413
msgid "external editors"
msgstr "Editor esterni"
#: ext_editor.lua:434
msgid "executable for external editor "
msgstr "Eseguibile per editor esterno "
#: ext_editor.lua:435
msgid "select executable for external editor"
msgstr "seleziona eseguibile per editor esterno "
#: ext_editor.lua:435
msgid "(None)"
msgstr "(Nessuno)"
#: ext_editor.lua:437
msgid "name of external editor "
msgstr "Nome editor esterno "
#: ext_editor.lua:438
msgid "friendly name of external editor"
msgstr "Nome comune editor esterno"
#: ext_editor.lua:444
msgid "edit with program "
msgstr "Modifica con programma "

View File

@@ -0,0 +1,190 @@
# French messages for script_manager.lua
# Christophe Agathon <christophe.agathon@gmail.com>, 2020.
# Maurizio Paglia <mpaglia0@gmail.com>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2020-12-28 10:22+0100\n"
"PO-Revision-Date: 2022-07-11 22:03+0200\n"
"Last-Translator: Maurizio Paglia <mpaglia0@gmail.com>\n"
"Language-Team: Italian <>\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: Lokalize 21.12.3\n"
"X-Poedit-Basepath: ../../../tools\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Poedit-KeywordsList: _\n"
"X-Poedit-SearchPath-0: script_manager.lua\n"
#: script_manager.lua:190
msgid "Cant read from "
msgstr "Non posso leggere da "
#: script_manager.lua:204
msgid "Loaded "
msgstr "Script caricato "
#: script_manager.lua:207
msgid " failed to load"
msgstr "Non riesco a caricare lo script"
#: script_manager.lua:223
msgid " will not start when darktable is restarted"
msgstr " non verrà avviato al riavvio di darktable"
#: script_manager.lua:281 script_manager.lua:345
msgid "find command is "
msgstr "Il comando find è "
#: script_manager.lua:321
msgid "lua scripts successfully updated"
msgstr "Gli script lua sono stati correttamente aggiornati"
#: script_manager.lua:381
msgid "category "
msgstr "Categoria"
#: script_manager.lua:381
msgid " is already in use. Please specify a different category name."
msgstr " è già utilizzato. Prego specificare un nome differente."
#: script_manager.lua:410
msgid "scripts successfully installed into category "
msgstr "Gli script sono stati installati correttamente nella categoria "
#: script_manager.lua:426
msgid "No scripts found to install"
msgstr "Non trovo script da installare"
#: script_manager.lua:430
msgid "failed to download scripts"
msgstr "Non riesco a scaricare gli script"
# Peut-être que « activé » serait mieux, mais ce n'est pas la traduction de « started »
#: script_manager.lua:458
msgid " started"
msgstr " avviato"
# Peut-être que « désactivé » serait mieux, mais ce n'est pas la traduction de « stopped »
#: script_manager.lua:460
msgid " stopped"
msgstr " bloccato"
#: script_manager.lua:545
msgid "Page "
msgstr "Pag."
#: script_manager.lua:545
msgid " of "
msgstr " di "
#: script_manager.lua:674
msgid "scripts to update"
msgstr "Script da aggiornare"
#: script_manager.lua:675
msgid "select the scripts installation to update"
msgstr "Selezionare l'installazione di script da aggiornare"
#: script_manager.lua:684 script_manager.lua:742
msgid "update scripts"
msgstr "Aggiornare gli script"
#: script_manager.lua:685
msgid "update the lua scripts from the repository"
msgstr "Aggiornare gli script lua del repo"
#: script_manager.lua:696
msgid ""
"enter the URL of the git repository containing the scripts you wish to add"
msgstr "Immettere l'URL del repo git che contiene gli script da aggiungere"
#: script_manager.lua:701
msgid "name of new category"
msgstr "Nome della nuova categoria"
#: script_manager.lua:702
msgid "enter a category name for the additional scripts"
msgstr "Immettere il nome della categoria per altri script"
#: script_manager.lua:707
msgid "URL to download additional scripts from"
msgstr "URL dal quale scaricare ulteriori script"
#: script_manager.lua:709
msgid "new category to place scripts in"
msgstr "Nuova categoria che dovrà contenere gli script"
#: script_manager.lua:712
msgid "install additional scripts"
msgstr "Installare gli script aggiuntivi"
#: script_manager.lua:720
msgid "Enable \"Disable Scripts\" button"
msgstr "Abilita il pulsante \"Disabilita Script\""
#: script_manager.lua:730
msgid "Disable Scripts"
msgstr "Disabilita Script"
#: script_manager.lua:736
msgid "lua scripts will not run the next time darktable is started"
msgstr "Gli script lua non verranno avviati al prossimo riavvio di darktable"
#: script_manager.lua:745
msgid "add more scripts"
msgstr "Aggiungi altri script"
#: script_manager.lua:747
msgid "disable scripts"
msgstr "Disabilita script"
#: script_manager.lua:755
msgid "category"
msgstr "categoria"
#: script_manager.lua:756
msgid "select the script category"
msgstr "Seleziona la categoria"
#: script_manager.lua:780
msgid "Page:"
msgstr "Pag.:"
#: script_manager.lua:808
msgid "Scripts"
msgstr "Scripts"
#: script_manager.lua:817
msgid "scripts per page"
msgstr "Script per pagina"
#: script_manager.lua:818
msgid "select number of start/stop buttons to display"
msgstr "Selezionare il numero di pulsanti da visualizzare"
#: script_manager.lua:829
msgid "change number of buttons"
msgstr "Modifica la quantità di pulsanti"
#: script_manager.lua:837
msgid "Configuration"
msgstr "Configurazione"
#: script_manager.lua:858
msgid "action"
msgstr "Azione"
#: script_manager.lua:864
msgid "install/update scripts"
msgstr "Installa/aggiorna script"
#: script_manager.lua:864
msgid "configure"
msgstr "Configura"
#: script_manager.lua:864
msgid "start/stop scripts"
msgstr "Avvia/blocca script"

View File

@@ -0,0 +1,46 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-09-28 19:54-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/AutoGrouper.lua:144
msgid "auto group"
msgstr "grupo automático"
#: ../contrib/AutoGrouper.lua:173
msgid "group gap [sec.]"
msgstr "intervalo de agrupamento [seg]"
#: ../contrib/AutoGrouper.lua:174
msgid "minimum gap, in seconds, between groups"
msgstr "intervalo mínimo, em segundos, entre os grupos"
#: ../contrib/AutoGrouper.lua:187
msgid "auto group: selected"
msgstr "grupo automático: selecionado"
#: ../contrib/AutoGrouper.lua:188
msgid "auto group selected images"
msgstr "agrupa automaticamente as imagens selecionadas"
#: ../contrib/AutoGrouper.lua:192
msgid "auto group: collection"
msgstr "grupo automático: coleção"
#: ../contrib/AutoGrouper.lua:193
msgid "auto group the entire collection"
msgstr "agrupa automaticamente toda a coleção"

View File

@@ -0,0 +1,97 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-09-28 20:02-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/CollectHelper.lua:74
msgid "Please select a single image"
msgstr "Por favor, selecione uma única imagem"
#: ../contrib/CollectHelper.lua:124
msgid "select an image with an active color label"
msgstr "selecione uma imagem com uma etiqueta de cor ativa"
#: ../contrib/CollectHelper.lua:205
msgid "collect: previous"
msgstr "colecionar: anterior"
#: ../contrib/CollectHelper.lua:207
msgid "Sets the Collect parameters to be the previously active parameters"
msgstr "Define os parâmetros da coleção para os ativos anteriormente"
#: ../contrib/CollectHelper.lua:211
msgid "collect: folder"
msgstr "colecionar: pasta"
#: ../contrib/CollectHelper.lua:213
msgid "Sets the Collect parameters to be the selected images's folder"
msgstr "Define os parâmetros da coleção para a pasta de imagens selecionada"
#: ../contrib/CollectHelper.lua:218
msgid "collect: color label(s)"
msgstr "colecionar: etiqueta(s) de cor"
#: ../contrib/CollectHelper.lua:220
msgid "Sets the Collect parameters to be the selected images's color label(s)"
msgstr ""
"Define os parâmetros da coleção para a(s) etiqueta(s) de cor das imagens"
" selecionadas"
#: ../contrib/CollectHelper.lua:225
msgid "collect: all (AND)"
msgstr "colecionar: tudo (E)"
#: ../contrib/CollectHelper.lua:227
msgid ""
"Sets the Collect parameters based on all activated CollectHelper options"
msgstr "Define os parâmetros da coleção baseado em todas as opções do script"
#: ../contrib/CollectHelper.lua:234
msgid "CollectHelper: All"
msgstr "CollectHelper: Tudo"
#: ../contrib/CollectHelper.lua:235
msgid ""
"Will create a collect parameter set that utilizes all enabled CollectHelper "
"types (AND)"
msgstr ""
"Criará um conjunto de parâmetros de coleção que utilizará todos os tipos "
"habilitados do CollectHelper (E)"
#: ../contrib/CollectHelper.lua:240
msgid "CollectHelper: Color Label(s)"
msgstr "CollectHelper: Etiqueta(s) de Cor"
#: ../contrib/CollectHelper.lua:241
msgid ""
"Enable the button that allows you to swap to a collection based on selected "
"image's COLOR LABEL(S)"
msgstr ""
"Habilita o botão que permite que você alterna para uma coleção baseada na "
"ETIQUETA(S) DE COR da imagem selecionada"
#: ../contrib/CollectHelper.lua:246
msgid "CollectHelper: Folder"
msgstr "CollectHelper: Pasta"
#: ../contrib/CollectHelper.lua:247
msgid ""
"Enable the button that allows you to swap to a collection based on selected "
"image's FOLDER location"
msgstr ""
"Habilita o botão que permite que você alterne para uma coleção baseada na "
"PASTA da imagem selecionada"

View File

@@ -0,0 +1,163 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 19:40-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/HDRMerge.lua:175
msgid "please update you binary location"
msgstr "por favor, atualize a localização do binário"
#: ../contrib/HDRMerge.lua:194
msgid "update successful"
msgstr "atualização bem sucedida"
#: ../contrib/HDRMerge.lua:196
msgid "update unsuccessful, please try again"
msgstr "atualização mau sucedida, tente de novo"
#: ../contrib/HDRMerge.lua:210
msgid "HDRMerge install issue"
msgstr "problema com a instalação do HDRMerge"
#: ../contrib/HDRMerge.lua:211
msgid "HDRMerge install issue, please ensure the binary path is proper"
msgstr ""
"problema com a instalação do HDRMerge, certifique-se de que o caminho do"
" binário esteja correto"
#: ../contrib/HDRMerge.lua:216
msgid "not enough images selected, select at least 2 images to merge"
msgstr ""
"imagens selecionadas insuficientes, selecione apenas 2 imagens para mesclar"
#: ../contrib/HDRMerge.lua:277
msgid "HDRMerge completed successfully"
msgstr "HDRMerge completado com sucesso"
#: ../contrib/HDRMerge.lua:279 ../contrib/HDRMerge.lua:280
msgid "HDRMerge failed"
msgstr "HDRMerge falhou"
#: ../contrib/HDRMerge.lua:289
msgid "HDRMerge"
msgstr "HDRMerge"
#: ../contrib/HDRMerge.lua:312
msgid "HDRMerge options"
msgstr "opções do HDRMerge"
#: ../contrib/HDRMerge.lua:317
msgid "bits per sample"
msgstr "bits por amostra"
#: ../contrib/HDRMerge.lua:318
msgid "number of bits per sample in the output image"
msgstr "número de bits por amostra na imagem de saída"
#: ../contrib/HDRMerge.lua:334
msgid "embedded preview size"
msgstr "tamanho da pré-visualização embutida"
#: ../contrib/HDRMerge.lua:335
msgid "size of the embedded preview in output image"
msgstr "tamanho da pré-visualização embutida na imagem de saída"
#: ../contrib/HDRMerge.lua:337 ../contrib/HDRMerge.lua:382
msgid "none"
msgstr "nenhum"
#: ../contrib/HDRMerge.lua:337
msgid "half"
msgstr "metade"
#: ../contrib/HDRMerge.lua:337
msgid "full"
msgstr "completo"
#: ../contrib/HDRMerge.lua:349
msgid "batch mode"
msgstr "modo em lote"
#: ../contrib/HDRMerge.lua:351
msgid ""
"enable batch mode operation \n"
"NOTE: resultant files will NOT be auto-imported"
msgstr ""
"ativar a operação em modo em lote\n"
"NOTA: arquivos resultantes NÃO serão importados automaticamente"
#: ../contrib/HDRMerge.lua:361
msgid "batch gap [sec.]"
msgstr "intervalo do lote [seg.]"
#: ../contrib/HDRMerge.lua:362
msgid "gap, in seconds, between batch mode groups"
msgstr "intervalo, em segundos, entre os grupos do modo em lote"
#: ../contrib/HDRMerge.lua:376
msgid "import options"
msgstr "opções de importação"
#: ../contrib/HDRMerge.lua:379
msgid "apply style on import"
msgstr "aplicar estilo ao importar"
#: ../contrib/HDRMerge.lua:380
msgid "Apply selected style on auto-import to newly created image"
msgstr ""
"Aplica o estilo selecionado ao importar automaticamente para novas imagens"
" criadas"
#: ../contrib/HDRMerge.lua:400
msgid "copy tags"
msgstr "copiar etiquetas"
#: ../contrib/HDRMerge.lua:402
msgid "copy tags from first source image"
msgstr "copia etiquetas da primeira imagem fonte"
#: ../contrib/HDRMerge.lua:409
msgid ""
"Additional tags to be added on import. Seperate with commas, all spaces will "
"be removed"
msgstr ""
"Etiquetas adicionais a serem anexadas ao importar. Separar com vírgulas,"
" todos os espaços serão removidos"
#: ../contrib/HDRMerge.lua:411
msgid "Enter tags, seperated by commas"
msgstr "Insira as etiquetas, separadas por vírgulas"
#: ../contrib/HDRMerge.lua:415
msgid "merge"
msgstr "mesclar"
#: ../contrib/HDRMerge.lua:416
msgid "run HDRMerge with the above specified settings"
msgstr "rodar o HDRMerge com as configurações especificadas acima"
#: ../contrib/HDRMerge.lua:420
msgid "Select HDRmerge executable"
msgstr "Selecionar o executável do HDRmerge"
#: ../contrib/HDRMerge.lua:425
msgid "update"
msgstr "atualizar"
#: ../contrib/HDRMerge.lua:426
msgid "update the binary path with current value"
msgstr "atualiza o caminho do binário com o valor atual"

View File

@@ -0,0 +1,86 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 23:40-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/OpenInExplorer.lua:79
msgid ""
"OpenInExplorer plug-in only supports Linux, macOS, and Windows at this time"
msgstr ""
"Atualmente, o plugin OpenInExplorer somente suporta Linux, macOS e Windows"
#: ../contrib/OpenInExplorer.lua:95
msgid ""
"No links directory selected.\n"
"Please check the dt preferences (lua options)"
msgstr ""
"Nenhum link para pasta selecionado.\n"
"Verifique as preferências do darktable (opções lua)"
#: ../contrib/OpenInExplorer.lua:99
#, lua-format
msgid ""
"Links directory '%s' not found.\n"
"Please check the dt preferences (lua options)"
msgstr ""
"Links para pasta '%s' não encontrados.\n"
"Verifique as preferências do darktable (opções lua)"
#: ../contrib/OpenInExplorer.lua:156
msgid "Failed to create links. Missing rights?"
msgstr "Falha ao criar links. Possui permissões adequadas?"
#: ../contrib/OpenInExplorer.lua:172
msgid "Please select an image"
msgstr "Por favor, selecione uma imagem"
#: ../contrib/OpenInExplorer.lua:178
msgid "Please select fewer images (max. 15)"
msgstr "Por favor, selecione menos imagens (máx 15)"
#: ../contrib/OpenInExplorer.lua:203
msgid "show in file explorer"
msgstr "mostrar no navegador de arquivos"
#: ../contrib/OpenInExplorer.lua:205
msgid "Open the file manager at the selected image's location"
msgstr "Abre o gerenciador de arquivos na localização das imagens selecionadas"
#: ../contrib/OpenInExplorer.lua:212
msgid "OpenInExplorer: linked files directory"
msgstr "OpenInExplorer: pasta de arquivos linkada"
#: ../contrib/OpenInExplorer.lua:213
msgid ""
"Directory to store the links to the file names. Requires restart to take "
"effect"
msgstr ""
"Pasta para armazenar os links para os nomes de arquivo. Precisa reiniciar"
" para ter efeito"
#: ../contrib/OpenInExplorer.lua:216
msgid "Select directory"
msgstr "Selecione a pasta"
#: ../contrib/OpenInExplorer.lua:222
msgid "OpenInExplorer: use links"
msgstr "OpenInExplorer: usar links"
#: ../contrib/OpenInExplorer.lua:223
msgid "Use links instead of multiple windows. Requires restart to take effect"
msgstr ""
"Usa links ao invés de janelas múltiplas. Precisa reiniciar para ter efeito"

View File

@@ -0,0 +1,74 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-02 05:31-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/RL_out_sharp.lua:119
msgid "GMic executable not configured"
msgstr "executável GMic não configurado"
#: ../contrib/RL_out_sharp.lua:139
msgid "sharpening image "
msgstr "melhorar nitidez da imagem "
#: ../contrib/RL_out_sharp.lua:154
msgid "sharpening error"
msgstr "erro ao melhorar nitidez"
#: ../contrib/RL_out_sharp.lua:163
msgid "finished exporting"
msgstr "exportação finalizada"
#: ../contrib/RL_out_sharp.lua:175 ../contrib/RL_out_sharp.lua:176
msgid "select output folder"
msgstr "selecionar pasta de saída"
#: ../contrib/RL_out_sharp.lua:185
msgid "sigma"
msgstr "sigma"
#: ../contrib/RL_out_sharp.lua:186
msgid "controls the width of the blur that's applied"
msgstr "controla a largura do desfoque que é aplicada"
#: ../contrib/RL_out_sharp.lua:197
msgid "iterations"
msgstr "iterações"
#: ../contrib/RL_out_sharp.lua:198
msgid "increase for better sharpening, but slower"
msgstr "aumente para melhor efeito, mas diminui velocidade"
#: ../contrib/RL_out_sharp.lua:209
msgid "output jpg quality"
msgstr "qualidade do jpg de saída"
#: ../contrib/RL_out_sharp.lua:210
msgid "quality of the output jpg file"
msgstr "qualidade do arquivo jpg de saída"
#: ../contrib/RL_out_sharp.lua:229
msgid "RL output sharpen"
msgstr "melhoria de nitidez na saída RL"
#: ../contrib/RL_out_sharp.lua:233
msgid "executable for GMic CLI"
msgstr "executável para o GMic CLI"
#: ../contrib/RL_out_sharp.lua:234
msgid "select executable for GMic command line version"
msgstr "selecione o executável para a versão de linha de comando do GMic"

View File

@@ -0,0 +1,30 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-09-28 19:53-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/clear_GPS.lua:81
msgid "clear GPS data"
msgstr "limpar dados de GPS"
#: ../contrib/clear_GPS.lua:83
msgid "Clear GPS data from selected images"
msgstr "Limpa os dados de GPS das imagens selecionadas"
#: ../contrib/clear_GPS.lua:89
msgid "Clear GPS data"
msgstr "Limpar dados de GPS"

View File

@@ -0,0 +1,90 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-09-30 19:05-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/color_profile_manager.lua:122
msgid "added color profile "
msgstr "perfil de cor adicionado "
#: ../contrib/color_profile_manager.lua:128
msgid "removed color profile "
msgstr "perfil de cor removido "
#: ../contrib/color_profile_manager.lua:146
msgid "ERROR: color profile must be an icc or icm file"
msgstr "ERRO: perfil de cor deve ser um arquivo icm ou icc"
#: ../contrib/color_profile_manager.lua:229
msgid "color profile manager"
msgstr "gerenciador de perfil de cor"
#: ../contrib/color_profile_manager.lua:270
msgid "initialize color profiles"
msgstr "inicializar perfis de cor"
#: ../contrib/color_profile_manager.lua:271
msgid "create the directory structure to contain the color profiles"
msgstr "cria a estrutura de pasta para conter os perfis de cor"
#: ../contrib/color_profile_manager.lua:286
msgid "select profile set"
msgstr "selecionar definição de perfil"
#: ../contrib/color_profile_manager.lua:287
msgid "select input or output profiles"
msgstr "selecionar perfis de entrada ou saída"
#: ../contrib/color_profile_manager.lua:294
msgid "input"
msgstr "entrada"
#: ../contrib/color_profile_manager.lua:294
msgid "output"
msgstr "saída"
#: ../contrib/color_profile_manager.lua:298
msgid "select color profile to add"
msgstr "selecionar perfil de cor a adicionar"
#: ../contrib/color_profile_manager.lua:299
msgid "select the .icc or .icm file to add"
msgstr "selecionar arquivo icc ou icm a adicionar"
#: ../contrib/color_profile_manager.lua:306
msgid "add profile"
msgstr "adicionar perfil"
#: ../contrib/color_profile_manager.lua:309
msgid "add selected color profile"
msgstr "adicionar perfil de cor selecionado"
#: ../contrib/color_profile_manager.lua:310
msgid "add selected file to profiles"
msgstr "adicionar arquivo selecionado aos perfis"
#: ../contrib/color_profile_manager.lua:320
msgid "remove profile"
msgstr "remover perfil"
#: ../contrib/color_profile_manager.lua:323
msgid "remove selected profile(s)"
msgstr "remover perfil(s) selecionado(s)"
#: ../contrib/color_profile_manager.lua:324
msgid "remove the checked profile(s)"
msgstr "remover o(s) perfil(s) marcado(s)"

View File

@@ -0,0 +1,86 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-09-28 19:52-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/copy_attach_detach_tags.lua:101
msgid "Image tags copied ..."
msgstr "Etiquetas de imagem copiadas ..."
#: ../contrib/copy_attach_detach_tags.lua:123
msgid "No tag to attach, please copy tags first."
msgstr "Nenhuma etiqueta anexada, por favor copie as etiquetas primeiro."
#: ../contrib/copy_attach_detach_tags.lua:147
msgid "Tags attached ..."
msgstr "Etiquetas anexadas ..."
#: ../contrib/copy_attach_detach_tags.lua:163
msgid "Tags removed from image(s)."
msgstr "Etiquetas removidas da(s) imagem(ns) "
#: ../contrib/copy_attach_detach_tags.lua:169
msgid "Tags replaced"
msgstr "Etiquetas substituídas"
#: ../contrib/copy_attach_detach_tags.lua:174
msgid "tagging addon"
msgstr "addon de etiquetas"
#: ../contrib/copy_attach_detach_tags.lua:204
#: ../contrib/copy_attach_detach_tags.lua:241
#: ../contrib/copy_attach_detach_tags.lua:294
msgid "copy tags from selected image(s)"
msgstr "copiar etiquetas da(s) imagem(ns) selecionada(s)"
#: ../contrib/copy_attach_detach_tags.lua:209
#: ../contrib/copy_attach_detach_tags.lua:244
#: ../contrib/copy_attach_detach_tags.lua:299
msgid "paste tags to selected image(s)"
msgstr "colar etiquetas para a(s) imagem(ns) selecionada(s)"
#: ../contrib/copy_attach_detach_tags.lua:214
#: ../contrib/copy_attach_detach_tags.lua:257
#: ../contrib/copy_attach_detach_tags.lua:304
msgid "remove tags from selected image(s)"
msgstr "remover etiquetas da(s) imagem(ns) selecionada(s)"
#: ../contrib/copy_attach_detach_tags.lua:219
#: ../contrib/copy_attach_detach_tags.lua:253
#: ../contrib/copy_attach_detach_tags.lua:309
msgid "replace tags from selected image(s)"
msgstr "substituir etiquetas a partir da(s) imagem(ns) selecionada(s)"
#: ../contrib/copy_attach_detach_tags.lua:232
msgid "tag clipboard"
msgstr "área de transferência de etiqueta"
#: ../contrib/copy_attach_detach_tags.lua:240
msgid "multi copy tags"
msgstr "copiar etiquetas múltiplas"
#: ../contrib/copy_attach_detach_tags.lua:245
msgid "paste tags"
msgstr "colar etiquetas"
#: ../contrib/copy_attach_detach_tags.lua:252
msgid "replace tags"
msgstr "substituir etiquetas"
#: ../contrib/copy_attach_detach_tags.lua:256
msgid "remove all tags"
msgstr "remover todas as etiquetas"

View File

@@ -0,0 +1,34 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 14:57-0300\n"
"PO-Revision-Date: 2021-09-28 19:38-0300\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"X-Generator: Lokalize 21.08.1\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: copy_paste_metadata.lua:131
msgid "copy metadata"
msgstr "copiar metadados"
#: copy_paste_metadata.lua:133
msgid "copy metadata of the first selected image"
msgstr "copiar metadados da primeira imagem selecionada"
#: copy_paste_metadata.lua:139
msgid "paste metadata"
msgstr "colar metadados"
#: copy_paste_metadata.lua:141
msgid "paste metadata to the selected images"
msgstr "colar metadados para as imagens selecionadas"

View File

@@ -0,0 +1,61 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 15:19-0300\n"
"PO-Revision-Date: 2021-09-28 19:39-0300\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"X-Generator: Lokalize 21.08.1\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: enfuse.lua:187
#, lua-format
msgid "Error writing to `%s`"
msgstr "Erro ao salvar `%s`"
#: enfuse.lua:210
#, lua-format
msgid "Converting raw file '%s' to tiff..."
msgstr "Converter arquivo raw `%s` para tiff..."
#: enfuse.lua:220
#, lua-format
msgid "Skipping %s..."
msgstr "Pulando %s..."
#: enfuse.lua:227
msgid "No suitable images selected, nothing to do for enfuse"
msgstr "Nenhuma imagem adequada selecionado, nada a fazer para o script"
#: enfuse.lua:233
#, lua-format
msgid "%d image(s) skipped"
msgstr "%d imagem(ns) pulada(s)"
#: enfuse.lua:253
msgid "Enfuse failed, see terminal output for details"
msgstr "Script falhou, veja a saída do terminal para detalhes"
#: enfuse.lua:265
msgid "enfuse was successful, resulting image has been imported"
msgstr "script foi bem sucedido, imagem resultante foi importada"
#: enfuse.lua:267
#, lua-format
msgid "enfuse: done, resulting image '%s' has been imported with id %d"
msgstr "enfuse: feito, imagem '%s' resultante foi importada com o id %d"
#: enfuse.lua:301
msgid "Could not find enfuse executable. Not loading enfuse exporter..."
msgstr ""
"Não foi possível encontrar o executável do enfuse. Exportador do enfuse não"
" carregado..."

View File

@@ -0,0 +1,555 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 19:03-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/enfuseAdvanced.lua:250
msgid "please update your binary locations"
msgstr "por favor, atualiza suas localizações de binários"
#: ../contrib/enfuseAdvanced.lua:262
msgid "issue with "
msgstr "problema com "
#: ../contrib/enfuseAdvanced.lua:262
msgid " executable"
msgstr " executável"
#: ../contrib/enfuseAdvanced.lua:271
msgid "update successful"
msgstr "atualização bem sucedida"
#: ../contrib/enfuseAdvanced.lua:273
msgid "update unsuccessful, please try again"
msgstr "atualização mau sucedida, tente novamente"
#: ../contrib/enfuseAdvanced.lua:379
msgid "saved to "
msgstr "salvar para "
#: ../contrib/enfuseAdvanced.lua:395
msgid "loaded from "
msgstr "carregado de "
#: ../contrib/enfuseAdvanced.lua:430
msgid "export for image fusion "
msgstr "exportar para fusão de imagem "
#: ../contrib/enfuseAdvanced.lua:435
msgid "too few images selected, please select at least 2 images"
msgstr "muito poucas imagens selecionadas, selecione pelo menos 2 imagens"
#: ../contrib/enfuseAdvanced.lua:438
msgid "installation error, please verify binary paths are proper"
msgstr ""
"erro de instalação, verifique se as localizações dos binários estão corretas"
#: ../contrib/enfuseAdvanced.lua:458 ../contrib/enfuseAdvanced.lua:459
#: ../contrib/enfuseAdvanced.lua:485 ../contrib/enfuseAdvanced.lua:486
msgid " failed"
msgstr " falhou"
#: ../contrib/enfuseAdvanced.lua:534
msgid "image align options"
msgstr "opções de alinhamento de imagem"
#: ../contrib/enfuseAdvanced.lua:537
msgid "align images"
msgstr "alinhar imagens"
#: ../contrib/enfuseAdvanced.lua:539
msgid "automatically align images prior to enfuse"
msgstr "alinha automaticamente as imagens antes de fundir"
#: ../contrib/enfuseAdvanced.lua:549
msgid "optimize radial distortion"
msgstr "otimizar distorção radial"
#: ../contrib/enfuseAdvanced.lua:551
msgid ""
"optimize radial distortion for all images, \n"
"except for first"
msgstr ""
"otimiza a distorção radial para todas as imagens, \n"
"exceto para a primeira"
#: ../contrib/enfuseAdvanced.lua:556
msgid "optimize field of view"
msgstr "otimizar o campo de visão"
#: ../contrib/enfuseAdvanced.lua:558
msgid ""
"optimize field of view for all images, except for first. \n"
"Useful for aligning focus stacks (DFF) with slightly \n"
"different magnification."
msgstr ""
"otimizar o campo de visão para todas as imagens, exceto para a primeira. \n"
"Útil para alinhar foco empilhado (DFF, sigla em inglês) com ampliação \n"
"ligeiramente diferente."
#: ../contrib/enfuseAdvanced.lua:563
msgid "optimize image center shift"
msgstr "otimizar deslocamento do centro da imagem"
#: ../contrib/enfuseAdvanced.lua:565
msgid ""
"optimize image center shift for all images, \n"
"except for first."
msgstr ""
"otimiza o deslocamento do centro da imagem para\n"
"todas as imagens, exceto a primeira."
#: ../contrib/enfuseAdvanced.lua:570
msgid "auto crop"
msgstr "recorte automático"
#: ../contrib/enfuseAdvanced.lua:572
msgid "auto crop the image to the area covered by all images."
msgstr ""
"recorta automaticamente a imagem para a área coberta por todas\n"
"as imagens."
#: ../contrib/enfuseAdvanced.lua:577
msgid "load distortion from lens database"
msgstr "carregar distorção a partir da base de dados de lentes"
#: ../contrib/enfuseAdvanced.lua:579
msgid "try to load distortion information from lens database"
msgstr "tenta carregar informações de distorção da base de dados de lentes"
#: ../contrib/enfuseAdvanced.lua:584
msgid "use gpu"
msgstr "usar gpu"
#: ../contrib/enfuseAdvanced.lua:586
msgid "use gpu during alignment"
msgstr "usa a gpu durante o alinhamento"
#: ../contrib/enfuseAdvanced.lua:593
msgid "image grid size"
msgstr "tamanho da grade de imagem"
#: ../contrib/enfuseAdvanced.lua:594
msgid ""
"break image into a rectangular grid \n"
"and attempt to find num control points in each section.\n"
"default: (5x5)"
msgstr ""
"quebra a imagem em uma grade retangular e \n"
"tenta encontrar um número de pontos de controle\n"
"em cada seção.\n"
"padrão: (5x5)"
#: ../contrib/enfuseAdvanced.lua:610
msgid "control points/grid"
msgstr "pontos de controle/grade"
#: ../contrib/enfuseAdvanced.lua:611
msgid ""
"number of control points (per grid, see option -g) \n"
"to create between adjacent images \n"
"default: (8)."
msgstr ""
"número de pontos de controle (por grade, veja a opção -g)\n"
"para criar entre imagens adjacentes \n"
"padrão: (8)"
#: ../contrib/enfuseAdvanced.lua:627
msgid "remove control points with error"
msgstr "remover pontos de controle com erro"
#: ../contrib/enfuseAdvanced.lua:628
msgid ""
"remove all control points with an error higher \n"
"than num pixels \n"
"default: (3)"
msgstr ""
"remove todos os pontos de controle com um erro superior\n"
"que um número de pixels\n"
"padrão: (3)"
#: ../contrib/enfuseAdvanced.lua:644
msgid "correlation threshold for control points"
msgstr "limiar de correlação para pontos de controle"
#: ../contrib/enfuseAdvanced.lua:645
msgid ""
"correlation threshold for identifying \n"
"control points \n"
"default: (0.9)."
msgstr ""
"limiar de correlação para identificar pontos\n"
"de controle\n"
"padrão: (0,9)"
#: ../contrib/enfuseAdvanced.lua:659
msgid "image fusion options"
msgstr "opções de fusão de imagem"
#: ../contrib/enfuseAdvanced.lua:664
msgid "exposure weight"
msgstr "peso da exposição"
#: ../contrib/enfuseAdvanced.lua:665
msgid ""
"set the relative weight of the well-exposedness criterion \n"
"as defined by the chosen exposure weight function. \n"
"increasing this weight relative to the others will\n"
" make well-exposed pixels contribute more to\n"
" the final output. \n"
"default: (1.0)"
msgstr ""
"configura o peso relativo do critério de boa exposição como\n"
"definido pela função de peso de exposição.\n"
"aumentar este peso relativo para outros fará com que\n"
"os pixels bem expostos contribuam mais para a saída\n"
"final.\n"
"padrão: (1,0)"
#: ../contrib/enfuseAdvanced.lua:676
msgid "saturation weight"
msgstr "peso da saturação"
#: ../contrib/enfuseAdvanced.lua:677
msgid ""
"set the relative weight of high-saturation pixels. \n"
"increasing this weight makes pixels with high \n"
"saturation contribute more to the final output. \n"
"default: (0.2)"
msgstr ""
"configura o peso relativo dos pixels altamente saturados.\n"
"aumentar este peso fará com que os pixels com alta saturação\n"
"contribuam mais para a saída final. "
"padrão: (0,2)"
#: ../contrib/enfuseAdvanced.lua:688
msgid "contrast weight"
msgstr "peso do contraste"
#: ../contrib/enfuseAdvanced.lua:689
msgid ""
"sets the relative weight of high local-contrast pixels. \n"
"default: (0.0)."
msgstr ""
"configura o peso relativo dos pixels com alto contraste local.\n"
"padrão: (0,0)"
#: ../contrib/enfuseAdvanced.lua:700
msgid "exposure optimum"
msgstr "exposição ótima"
#: ../contrib/enfuseAdvanced.lua:701
msgid ""
"determine at what normalized exposure value\n"
" the optimum exposure of the input images\n"
" is. this is, set the position of the maximum\n"
" of the exposure weight curve. use this \n"
"option to fine-tune exposure weighting. \n"
"default: (0.5)"
msgstr ""
"determina qual valor de exposição normalizado\n"
"é a exposição ótima da imagem de entrada.\n"
"isto é, define a posição do máximo de exposição\n"
"da curva de peso. use esta opção para ajustar o\n"
"peso da exposição.\n"
"padrão: (0,5)"
#: ../contrib/enfuseAdvanced.lua:712
msgid "exposure width"
msgstr "largura da exposição"
#: ../contrib/enfuseAdvanced.lua:713
msgid ""
"set the characteristic width (FWHM) of the exposure \n"
"weight function. low numbers give less weight to \n"
"pixels that are far from the user-defined \n"
"optimum and vice versa. use this option to \n"
"fine-tune exposure weighting. \n"
"default: (0.2)"
msgstr ""
"configura a largura característica (FWHM) da função de peso\n"
"da exposição. números baixos atribuem menos peso para os\n"
"pixels que estão longe do ótimo definido pelo usuário e vice-\n"
"versa. use esta opção para ajustar o peso da exposição.\n"
"padrão: (0,2)"
#: ../contrib/enfuseAdvanced.lua:722
msgid "hard mask"
msgstr "máscara dura"
#: ../contrib/enfuseAdvanced.lua:724
msgid ""
"force hard blend masks on the finest scale. this avoids \n"
"averaging of fine details (only), at the expense \n"
"of increasing the noise. this improves the \n"
"sharpness of focus stacks considerably.\n"
"default (soft mask)"
msgstr ""
"força máscaras de mesclagem duras na escala mais fina. isto\n"
"previne aproximação dos detalhes mais finos (somente), ao\n"
"custo de aumentar o ruído. isto melhora a nitidez do empilha-\n"
"mento de foco consideravelmente.\n"
"padrão (máscara suave)"
#: ../contrib/enfuseAdvanced.lua:729
msgid "save masks"
msgstr "salvar máscaras"
#: ../contrib/enfuseAdvanced.lua:731
msgid ""
"Save the generated weight masks to your home directory,\n"
"enblend saves masks as 8 bit grayscale, \n"
"i.e. single channel images. \n"
"for accuracy we recommend to choose a lossless format."
msgstr ""
"Salva as máscaras de peso geradas para sua pasta pessoal,\n"
"mescla máscaras salvas como tons de cinza de 8 bits, isto é,\n"
"imagens de canal simples.\n"
"para maior precisão, nós recomendamos escolher um formato\n"
"sem perdas."
#: ../contrib/enfuseAdvanced.lua:738
msgid "contrast window size"
msgstr "tamanho da janela de contraste"
#: ../contrib/enfuseAdvanced.lua:739
msgid ""
"set the window size for local contrast analysis. \n"
"the window will be a square of size × size pixels. \n"
"if given an even size, Enfuse will \n"
"automatically use the next odd number.\n"
"for contrast analysis size values larger \n"
"than 5 pixels might result in a \n"
"blurry composite image. values of 3 and \n"
"5 pixels have given good results on \n"
"focus stacks. \n"
"default: (5) pixels"
msgstr ""
"configura o tamanho da janela para a análise de contraste local.\n"
"a janela será um quadrado de tamanho x tamanho em pixels.\n"
"se fornecido um tamanho par, o script usará automaticamente\n"
"o próximo número ímpar.\n"
"valores de tamanho para análise de contraste maiores que\n"
"5 pixels podem resultar em uma imagem composta borrada.\n"
"valores de 3 e 5 pixels produzem bons resultados para empi-\n"
"lhamento de foco.\n"
"padrão: (5) pixels"
#: ../contrib/enfuseAdvanced.lua:755
msgid "contrast edge scale"
msgstr "escala de borda de contraste"
#: ../contrib/enfuseAdvanced.lua:756
msgid ""
"a non-zero value for EDGE-SCALE switches on the \n"
"Laplacian-of-Gaussian (LoG) edge detection algorithm.\n"
" edage-scale is the radius of the Gaussian used \n"
"in the search for edges. a positive LCE-SCALE \n"
"turns on local contrast enhancement (LCE) \n"
"before the LoG edge detection. \n"
"Default: (0.0) pixels."
msgstr ""
"um valor diferente de zero para a ESCALA DE BORDA alterna\n"
"o algoritmo de detecção de borda Laplaciano-de-Gaussiano (LoG).\n"
"a escala de borda é o raio do Gaussiano usado na busca por bordas.\n"
"uma escala LCE positiva ativa o melhoramento de contraste local (LCE)\n"
"antes da detecção de borda LoG.\n"
"Padrão: (0,0) pixels"
#: ../contrib/enfuseAdvanced.lua:772
msgid "contrast min curvature [%]"
msgstr "curvatura mín de contraste [%]"
#: ../contrib/enfuseAdvanced.lua:773
msgid ""
"define the minimum curvature for the LoG edge detection. Append a ‘%’ to "
"specify the minimum curvature relative to maximum pixel value in the source "
"image. Default: (0.0%)"
msgstr ""
"configura a curvatura mínima para a detecção de borda LoG. Adicionar um '%'"
" para "
"definir a curvatura mínima em relação ao valor máximo de pixel na imagem"
" fonte. "
"Padrão: (0,0%)"
#: ../contrib/enfuseAdvanced.lua:787
msgid "target file"
msgstr "arquivo alvo"
#: ../contrib/enfuseAdvanced.lua:792
msgid "tiff compression"
msgstr "compressão tiff"
#: ../contrib/enfuseAdvanced.lua:793
msgid "compression method for tiff files"
msgstr "método de compressão para arquivos tiff"
#: ../contrib/enfuseAdvanced.lua:809
msgid "jpeg compression"
msgstr "compressão jpeg"
#: ../contrib/enfuseAdvanced.lua:810
msgid "jpeg compression level"
msgstr "nível de compressão jpeg"
#: ../contrib/enfuseAdvanced.lua:832
msgid "file format"
msgstr "formato de arquivo"
#: ../contrib/enfuseAdvanced.lua:833
msgid "file format of the enfused final image"
msgstr "formato de arquivo da imagem final fundida"
#: ../contrib/enfuseAdvanced.lua:853
msgid "bit depth"
msgstr "profundidade de bits"
#: ../contrib/enfuseAdvanced.lua:854
msgid "bit depth of the enfused file"
msgstr "profundidade de bits do arquivo fundido"
#: ../contrib/enfuseAdvanced.lua:868
msgid "directory"
msgstr "pasta"
#: ../contrib/enfuseAdvanced.lua:877
msgid ""
"select the target directory for the fused image. \n"
"the filename is created automatically."
msgstr ""
"seleciona a pasta alvo para a imagem fundida.\n"
"o nome do arquivo é criado automaticamente."
#: ../contrib/enfuseAdvanced.lua:882
msgid "save to source image location"
msgstr "salvar para a localização da imagem fonte"
#: ../contrib/enfuseAdvanced.lua:884
msgid ""
"If checked ignores the location above and saves output image(s) to the same "
"location as the source images."
msgstr ""
"Se selecionado, ignora a localização acima e salva a(s) imagem(ns) de saída"
" na mesma "
"localização das imagens fonte."
#: ../contrib/enfuseAdvanced.lua:891
msgid "on conflict"
msgstr "no caso de conflito"
#: ../contrib/enfuseAdvanced.lua:905
msgid "auto import"
msgstr "importar automaticamente"
#: ../contrib/enfuseAdvanced.lua:907
msgid "import the image into darktable database when enfuse completes"
msgstr ""
"importa a imagem para a base de dados do darktable quando a fusão for"
" completada"
#: ../contrib/enfuseAdvanced.lua:918
msgid "Apply Style on Import"
msgstr "Aplicar estilo ao importar"
#: ../contrib/enfuseAdvanced.lua:919
msgid "Apply selected style on auto-import to newly created blended image"
msgstr ""
"Aplica o estilo selecionado ao importar automaticamente para a nova imagem"
" fundida criada"
#: ../contrib/enfuseAdvanced.lua:938
msgid "copy tags"
msgstr "copiar etiquetas"
#: ../contrib/enfuseAdvanced.lua:940
msgid "Copy tags from first image."
msgstr "Copia as etiquetas da primeira imagem."
#: ../contrib/enfuseAdvanced.lua:947
msgid ""
"Additional tags to be added on import. Seperate with commas, all spaces will "
"be removed"
msgstr ""
"Etiquetas adicionais a serem adicionadas ao importar. Separe-as com vírgulas,"
" todos "
"os espaços serão removidos"
#: ../contrib/enfuseAdvanced.lua:955
msgid "active preset"
msgstr "predefinição ativa"
#: ../contrib/enfuseAdvanced.lua:956
msgid "preset to be loaded from or saved to"
msgstr "predefinição a ser carregada de ou salvada para"
#: ../contrib/enfuseAdvanced.lua:970
msgid "load fusion preset"
msgstr "carregar predefinição de fusão"
#: ../contrib/enfuseAdvanced.lua:971
msgid "load current fusion parameters from selected preset"
msgstr ""
"carrega parâmetros de fusão atuais a partir da predefinição selecionada"
#: ../contrib/enfuseAdvanced.lua:975
msgid "save to fusion preset"
msgstr "salvar para predefinição de fusão"
#: ../contrib/enfuseAdvanced.lua:976
msgid "save current fusion parameters to selected preset"
msgstr "salva os parâmetros de fusão atuais para a predefinição selecionada"
#: ../contrib/enfuseAdvanced.lua:980
msgid "create image variants from presets"
msgstr "criar variantes da imagem a partir das predefinições"
#: ../contrib/enfuseAdvanced.lua:982
msgid ""
"create multiple image variants based on the three different presets of the "
"specified type"
msgstr ""
"cria múltiplas variantes da imagem baseadas nas três diferentes predefinições"
" do tipo "
"especificado"
#: ../contrib/enfuseAdvanced.lua:999
msgid "create variants type"
msgstr "criar tipos de variantes"
#: ../contrib/enfuseAdvanced.lua:1000
msgid "preset type to be used when creating image variants"
msgstr "tipo de predefinição a ser usado ao criar variantes de imagens"
#: ../contrib/enfuseAdvanced.lua:1039
msgid "update"
msgstr "atualizar"
#: ../contrib/enfuseAdvanced.lua:1040
msgid "update the binary paths with current values"
msgstr "atualiza os caminhos dos binários com os valores atuais"
#: ../contrib/enfuseAdvanced.lua:1120
msgid "show options"
msgstr "mostrar opções"
#: ../contrib/enfuseAdvanced.lua:1121
msgid "show options for specified aspect of output"
msgstr "mostra as opções para o aspecto de saída especificado"
#: ../contrib/enfuseAdvanced.lua:1142
msgid "DRI or DFF image"
msgstr "imagem DRI ou DFF"

View File

@@ -0,0 +1,58 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 11:50-0300\n"
"PO-Revision-Date: 2021-09-28 19:39-0300\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"X-Generator: Lokalize 21.08.1\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: executable_manager.lua:144
msgid "No executable paths found, exiting..."
msgstr "Caminho do executável não encontrado, saindo..."
#: executable_manager.lua:160
msgid "select an executable"
msgstr "selecione um executável"
#: executable_manager.lua:160
msgid "search path for executable"
msgstr "caminho de pesquisa por executável"
#: executable_manager.lua:169
msgid "select "
msgstr "selecionar "
#: executable_manager.lua:169
msgid " executable"
msgstr " executável"
#: executable_manager.lua:190
msgid "select executable to modify"
msgstr "selecione executável a modificar"
#: executable_manager.lua:205
msgid "current"
msgstr "atual"
#: executable_manager.lua:207
msgid "select"
msgstr "selecionar"
#: executable_manager.lua:209
msgid "reset"
msgstr "reiniciar"
#: executable_manager.lua:212
msgid "Clear path for "
msgstr "Limpar caminho para "

View File

@@ -0,0 +1,62 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-09-30 19:25-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/exportLUT.lua:66
msgid "Identity_file_chooser"
msgstr "Selecionar_arquiv_identidade"
#: ../contrib/exportLUT.lua:72
msgid "Export_location_chooser"
msgstr "Selecionar_localização_exportação"
#: ../contrib/exportLUT.lua:78
msgid "choose the identity haldclut file"
msgstr "selecione o arquivo halclut de identidade"
#: ../contrib/exportLUT.lua:82
msgid "choose the output location"
msgstr "selecione a localização de saída"
#: ../contrib/exportLUT.lua:86
msgid "WARNING: files may be silently overwritten"
msgstr "AVISO: os arquivos podem ser sobrescritos sem confirmação"
#: ../contrib/exportLUT.lua:108
msgid "Invalid identity lut file"
msgstr "Arquivo lut de identidade inválido"
#: ../contrib/exportLUT.lua:110
msgid "Exporting styles as haldCLUTs"
msgstr "Exportar estilos como haldCLUTs"
#: ../contrib/exportLUT.lua:129
msgid "Exported: "
msgstr "Exportado: "
#: ../contrib/exportLUT.lua:131
msgid "Done exporting haldCLUTs"
msgstr "Exportação de haldCLUTs feita"
#: ../contrib/exportLUT.lua:141
msgid "export haldclut"
msgstr "exportar haldclut"
#: ../contrib/exportLUT.lua:166
msgid "export"
msgstr "exportar"

View File

@@ -0,0 +1,129 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 19:07-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/ext_editor.lua:158
msgid " editors configured"
msgstr " editores configurados"
#: ../contrib/ext_editor.lua:167
msgid "not a valid choice"
msgstr "não é uma escolha válida"
#: ../contrib/ext_editor.lua:173
msgid "please select one image"
msgstr "por favor, selecione uma imagem"
#: ../contrib/ext_editor.lua:197
msgid "file type not allowed"
msgstr "tipo de arquivo não permitido"
#: ../contrib/ext_editor.lua:225
msgid "error copying file "
msgstr "erro ao copiar arquivo "
#: ../contrib/ext_editor.lua:232
msgid "launching "
msgstr "iniciando "
#: ../contrib/ext_editor.lua:235
msgid "error launching "
msgstr "erro ao lançar "
#: ../contrib/ext_editor.lua:328
msgid "error moving file "
msgstr "erro ao mover arquivo "
#: ../contrib/ext_editor.lua:337
msgid "finished exporting"
msgstr "exportação terminada"
#: ../contrib/ext_editor.lua:354
msgid "external editors"
msgstr "editores externos"
#: ../contrib/ext_editor.lua:380 ../contrib/ext_editor.lua:493
msgid "edit with program "
msgstr "editar com programa "
#: ../contrib/ext_editor.lua:382 ../contrib/ext_editor.lua:472
msgid "collection"
msgstr "coleção"
#: ../contrib/ext_editor.lua:393
msgid "choose program"
msgstr "selecionar programa"
#: ../contrib/ext_editor.lua:394
msgid "select the external editor from the list"
msgstr "selecione o editor externo a partir da lista"
#: ../contrib/ext_editor.lua:404
msgid "edit"
msgstr "editar"
#: ../contrib/ext_editor.lua:405
msgid "open the selected image in external editor"
msgstr "abre a imagem selecionada no editor externo"
#: ../contrib/ext_editor.lua:415
msgid "edit a copy"
msgstr "editar uma cópia"
#: ../contrib/ext_editor.lua:416
msgid "create a copy of the selected image and open it in external editor"
msgstr "cria uma cópia da imagem selecionada e abre-a no editor externo"
#: ../contrib/ext_editor.lua:425
msgid "update list"
msgstr "atualizar lista"
#: ../contrib/ext_editor.lua:426
msgid "update list of programs if lua preferences are changed"
msgstr "atualiza lista de programas se as preferências do lua forem alteradas"
#: ../contrib/ext_editor.lua:478
msgid "executable for external editor "
msgstr "executável para o editor externo "
#: ../contrib/ext_editor.lua:479
msgid "select executable for external editor"
msgstr "seleciona o executável para o editor externo"
#: ../contrib/ext_editor.lua:479
msgid "(None)"
msgstr "(Nenhum)"
#: ../contrib/ext_editor.lua:482
msgid "name of external editor "
msgstr "nome do editor externo "
#: ../contrib/ext_editor.lua:483
msgid "friendly name of external editor"
msgstr "nome amigável do editor externo"
#: ../contrib/ext_editor.lua:486
msgid "show external editors in darkroom"
msgstr "mostrar editores externos na sala escura"
#: ../contrib/ext_editor.lua:487
msgid ""
"check to show external editors module also in darkroom (requires restart)"
msgstr ""
"ative para mostrar o módulo de editores externos também na sala escura"
" (precisa reiniciar)"

View File

@@ -0,0 +1,135 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 19:12-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/face_recognition.lua:131
msgid "export images"
msgstr "exportar imagens"
#: ../contrib/face_recognition.lua:137
#, lua-format
msgid "Exporting image %i of %i images"
msgstr "Exportando imagem %i de %i imagens"
#: ../contrib/face_recognition.lua:207
msgid "Face recognition not found"
msgstr "Reconhecimento de rosto não encontrado"
#: ../contrib/face_recognition.lua:259
msgid "Starting face recognition..."
msgstr "Iniciando reconhecimento de rosto..."
#: ../contrib/face_recognition.lua:267
msgid "Face recognition failed"
msgstr "Reconhecimento de rosto falhou"
#: ../contrib/face_recognition.lua:269
msgid "Face recognition finished"
msgstr "Reconhecimento de rosto terminado"
#: ../contrib/face_recognition.lua:274
msgid "processing results..."
msgstr "processando resultados..."
#: ../contrib/face_recognition.lua:338
msgid "face recognition complete"
msgstr "reconhecimento de rosto completo"
#: ../contrib/face_recognition.lua:340
msgid "image export failed"
msgstr "exportação da imagem falhou"
#: ../contrib/face_recognition.lua:344
msgid "no images selected"
msgstr "nenhuma imagem selecionada"
#: ../contrib/face_recognition.lua:353
msgid "face recognition"
msgstr "reconhecimento de rosto"
#: ../contrib/face_recognition.lua:377
msgid "tag to be used for unknown person"
msgstr "etiqueta a ser usada para pessoa desconhecida"
#: ../contrib/face_recognition.lua:383
msgid "tag to be used when no persons are found"
msgstr "etiqueta a ser usada quando nenhuma pessoa for encontrada"
#: ../contrib/face_recognition.lua:389 ../contrib/face_recognition.lua:467
msgid "tags of images to ignore"
msgstr "etiquetas de imagens para ignorar"
#: ../contrib/face_recognition.lua:395 ../contrib/face_recognition.lua:469
msgid "tag category"
msgstr "categoria da etiqueta"
#: ../contrib/face_recognition.lua:400
msgid "tolerance"
msgstr "tolerância"
#: ../contrib/face_recognition.lua:412
msgid "processor cores"
msgstr "núcleos de processador"
#: ../contrib/face_recognition.lua:413
msgid "number of processor cores to use, 0 for all"
msgstr "número de núcleos de processador a usar, 0 para todos"
#: ../contrib/face_recognition.lua:424
msgid "known image directory"
msgstr "pasta de imagem conhecida"
#: ../contrib/face_recognition.lua:425 ../contrib/face_recognition.lua:471
msgid "face data directory"
msgstr "pasta de dados de rosto"
#: ../contrib/face_recognition.lua:434
msgid "export image format"
msgstr "formato de exportação da imagem"
#: ../contrib/face_recognition.lua:435
msgid "format for exported images"
msgstr "formato para as imagens exportadas"
#: ../contrib/face_recognition.lua:445
msgid "maximum exported image width"
msgstr "largura máxima das imagens exportadas"
#: ../contrib/face_recognition.lua:451
msgid "maximum exported image height"
msgstr "altura máxima das imagens exportada"
#: ../contrib/face_recognition.lua:463
msgid "unknown person tag"
msgstr "etiqueta de pessoa desconhecida"
#: ../contrib/face_recognition.lua:465
msgid "no persons found tag"
msgstr "etiqueta de nenhuma pessoa encontrada"
#: ../contrib/face_recognition.lua:478
msgid "processing options"
msgstr "opções de processamento"
#: ../contrib/face_recognition.lua:484
msgid "width "
msgstr "largura "
#: ../contrib/face_recognition.lua:489
msgid "height "
msgstr "altura "

View File

@@ -0,0 +1,30 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-09-30 19:26-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/fujifilm_ratings.lua:49
msgid "exiftool not found"
msgstr "exiftool não encontrado"
#: ../contrib/fujifilm_ratings.lua:62
msgid "Using JPEG Rating: "
msgstr "Usar classificação JPEG: "
#: ../contrib/fujifilm_ratings.lua:73
msgid "Using RAF Rating: "
msgstr "Usar classificação RAF: "

View File

@@ -0,0 +1,77 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 19:16-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/geoJSON_export.lua:82
#, lua-format
msgid "Export Image %i/%i"
msgstr "Exportar Imagem %i/%i"
#: ../contrib/geoJSON_export.lua:87
msgid "mkdir not found"
msgstr "mkdir não encontrado"
#: ../contrib/geoJSON_export.lua:91
msgid "convert not found"
msgstr "convert não encontrado"
#: ../contrib/geoJSON_export.lua:95
msgid "xdg-open not found"
msgstr "xdg-open não encontrado"
#: ../contrib/geoJSON_export.lua:99
msgid "xdg-user-dir not found"
msgstr "xdg-user-dir não encontrado"
#: ../contrib/geoJSON_export.lua:310
msgid "geoJSON export: Create an additional HTML file"
msgstr "exportar geoJSON: Criar um arquivo HTML adicional"
#: ../contrib/geoJSON_export.lua:311
msgid "Creates a HTML file, that loads the geoJASON file. (Needs a MapBox key"
msgstr ""
"Cria um arquivo HTML que carrega o arquivo geoJSON. (Precisa de uma chave"
" MapBox)"
#: ../contrib/geoJSON_export.lua:316
msgid "geoJSON export: MapBox Key"
msgstr "exportar geoJSON : chave MapBox"
#: ../contrib/geoJSON_export.lua:317
msgid "https://www.mapbox.com/studio/account/tokens"
msgstr "https://www.mapbox.com/studio/account/tokens"
#: ../contrib/geoJSON_export.lua:322
msgid "geoJSON export: Open geoJSON file after export"
msgstr "exportar geoJSON: Abrir o arquivo geoJSON após exportar"
#: ../contrib/geoJSON_export.lua:323
msgid ""
"Opens the geoJSON file after the export with the standard program for "
"geoJSON files"
msgstr ""
"Abre o arquivo geoJSON após a exportação com o programa padrão para arquivos"
" geoJSON"
#: ../contrib/geoJSON_export.lua:335
msgid "geoJSON export: Export directory"
msgstr "exportar geoJSON: Pasta de exportação"
#: ../contrib/geoJSON_export.lua:336
msgid "A directory that will be used to export the geoJSON files"
msgstr "Uma pasta que será usada para exportar os arquivos geoJSON"

View File

@@ -0,0 +1,166 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 19:23-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/geoToolbox.lua:58
msgid "Distance:"
msgstr "Distância:"
#: ../contrib/geoToolbox.lua:62
msgid "latitude:"
msgstr "latitude:"
#: ../contrib/geoToolbox.lua:67
msgid "longitude:"
msgstr "longitude:"
#: ../contrib/geoToolbox.lua:72
msgid "elevation:"
msgstr "elevação:"
#: ../contrib/geoToolbox.lua:79 ../contrib/geoToolbox.lua:99
msgid "GPS selection"
msgstr "seleção do GPS"
#: ../contrib/geoToolbox.lua:302
msgid "latitude: "
msgstr "latitude: "
#: ../contrib/geoToolbox.lua:303
msgid "longitude: "
msgstr "longitude: "
#: ../contrib/geoToolbox.lua:304
msgid "elevation: "
msgstr "elevação: "
#: ../contrib/geoToolbox.lua:329
msgid "gnome-maps not found"
msgstr "gnome-maps não encontrado"
#: ../contrib/geoToolbox.lua:367
msgid "curl not found"
msgstr "curl não encontrado"
#: ../contrib/geoToolbox.lua:372
msgid "jq not found"
msgstr "jq não encontrado"
#: ../contrib/geoToolbox.lua:490
msgid "m"
msgstr "m"
#: ../contrib/geoToolbox.lua:492
msgid "km"
msgstr "km"
#: ../contrib/geoToolbox.lua:495
#, lua-format
msgid "Distance: %.2f %s"
msgstr "Distância: %.2f %s"
#: ../contrib/geoToolbox.lua:508
msgid "export altitude CSV"
msgstr "exportar altitude para CSV"
#: ../contrib/geoToolbox.lua:518
msgid "Name of the exported file"
msgstr "Nome do arquivo exportado"
#: ../contrib/geoToolbox.lua:523
msgid "Start export"
msgstr "Iniciar exportação"
#: ../contrib/geoToolbox.lua:584
msgid "File created in "
msgstr "Arquivo criado em "
#: ../contrib/geoToolbox.lua:614 ../contrib/geoToolbox.lua:746
msgid "Calculate the distance from latitude and longitude in km"
msgstr "Calcula a distância da latitude e longitude em km"
#: ../contrib/geoToolbox.lua:619 ../contrib/geoToolbox.lua:644
#: ../contrib/geoToolbox.lua:751
msgid "Select all images with GPS information"
msgstr "Selecionar todas as imagens com informação de GPS"
#: ../contrib/geoToolbox.lua:621 ../contrib/geoToolbox.lua:650
#: ../contrib/geoToolbox.lua:753
msgid "Select all images without GPS information"
msgstr "Selecionar todas as imagens sem informação de GPS"
#: ../contrib/geoToolbox.lua:643
msgid "select geo images"
msgstr "selecionar imagens geoetiquetadas"
#: ../contrib/geoToolbox.lua:649
msgid "select non-geo images"
msgstr "selecionar imagens sem geoetiquetas"
#: ../contrib/geoToolbox.lua:656
msgid "copy GPS data"
msgstr "copiar dados do GPS"
#: ../contrib/geoToolbox.lua:657
msgid "Copy GPS data"
msgstr "Copiar dados do GPS"
#: ../contrib/geoToolbox.lua:665
msgid "paste GPS data"
msgstr "colar dados do GPS"
#: ../contrib/geoToolbox.lua:666
msgid "Paste GPS data"
msgstr "Colar dados do GPS"
#: ../contrib/geoToolbox.lua:695
msgid "open in Gnome Maps"
msgstr "abrir no Gnome Maps"
#: ../contrib/geoToolbox.lua:696
msgid "Open location in Gnome Maps"
msgstr "Abrir localização no Gnome Maps"
#: ../contrib/geoToolbox.lua:702
msgid "reverse geocode"
msgstr "geocódigo reverso"
#: ../contrib/geoToolbox.lua:703
msgid "This just shows the name of the location, but doesn't add it as tag"
msgstr ""
"Isto apenas mostra o nome da localização, mas não adiciona-a como uma etiqueta"
#: ../contrib/geoToolbox.lua:707
msgid "altitude CSV export"
msgstr "exportar altitude em CSV"
#: ../contrib/geoToolbox.lua:712
msgid "export altitude CSV file"
msgstr "exporta o arquivo CSV de altitude"
#: ../contrib/geoToolbox.lua:713
msgid "Create an altitude profile using the GPS data in the metadata"
msgstr "Cria um perfil de altitude usando os dados GPS nos metadados"
#: ../contrib/geoToolbox.lua:740
msgid "geoToolbox export: MapBox Key"
msgstr "exportar geoToolbox: chave MapBox"
#: ../contrib/geoToolbox.lua:741
msgid "https://www.mapbox.com/studio/account/tokens"
msgstr "https://www.mapbox.com/studio/account/tokens"

View File

@@ -0,0 +1,43 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 19:18-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/gimp.lua:111
#, lua-format
msgid "Export Image %i/%i"
msgstr "Exportar Imagem %i/%i"
#: ../contrib/gimp.lua:121
msgid "GIMP not found"
msgstr "GIMP não encontrado"
#: ../contrib/gimp.lua:144
msgid "Launching GIMP..."
msgstr "Iniciando GIMP..."
#: ../contrib/gimp.lua:207
msgid "run detached"
msgstr "executar desassociado"
#: ../contrib/gimp.lua:208
msgid "don't import resulting image back into darktable"
msgstr "não importar a imagem resultante de volta para o darktable"
#: ../contrib/gimp.lua:215
msgid "Edit with GIMP"
msgstr "Editar com o GIMP"

View File

@@ -0,0 +1,54 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-09-28 19:48-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/gpx_export.lua:59
msgid "gpx file path"
msgstr "caminho do arquivo gpx"
#: ../contrib/gpx_export.lua:72
msgid "exporting gpx file..."
msgstr "exportar arquivo gpx..."
#: ../contrib/gpx_export.lua:74
msgid "gpx export"
msgstr "exportar gpx"
#: ../contrib/gpx_export.lua:93 ../contrib/gpx_export.lua:94
msgid " does not have date information and won't be processed"
msgstr " não possui informações de data e não será processado"
#: ../contrib/gpx_export.lua:134
msgid "invalid path: "
msgstr "caminho inválido: "
#: ../contrib/gpx_export.lua:138
msgid "gpx file created: "
msgstr "arquivo gpx criado: "
#: ../contrib/gpx_export.lua:171
msgid "export"
msgstr "exportar"
#: ../contrib/gpx_export.lua:172
msgid "export gpx file"
msgstr "exportar arquivo gpx"
#: ../contrib/gpx_export.lua:180
msgid "file:"
msgstr "arquivo:"

View File

@@ -0,0 +1,22 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-09-30 19:26-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/harmonic_armature_guide.lua:76
msgid "harmonic armature"
msgstr "armadura harmônica"

View File

@@ -0,0 +1,67 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 19:26-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/hugin.lua:109
msgid "hugin is not found, did you set the path?"
msgstr "hugin não encontrado, você configurou o caminho?"
#: ../contrib/hugin.lua:165
msgid "will try to stitch now"
msgstr "tentará pregar agora"
#: ../contrib/hugin.lua:170
msgid "creating pto file"
msgstr "criar arquivo pto"
#: ../contrib/hugin.lua:174
msgid "running assistant"
msgstr "rodar assistente"
#: ../contrib/hugin.lua:182
msgid "launching hugin"
msgstr "iniciar o hugin"
#: ../contrib/hugin.lua:184
msgid "unable to find command line tools, launching hugin"
msgstr ""
"não foi possível encontrar as ferramentas de linha de comando, iniciar o hugin"
#: ../contrib/hugin.lua:192
msgid "hugin isn't available."
msgstr "hugin não está disponível."
#: ../contrib/hugin.lua:197
msgid "hugin failed ..."
msgstr "hugin falhou..."
#: ../contrib/hugin.lua:203
msgid "importing file "
msgstr "importar arquivo "
#: ../contrib/hugin.lua:229
msgid " launch hugin gui"
msgstr " iniciar interface gráfica do hugin"
#: ../contrib/hugin.lua:231
msgid "launch hugin in gui mode"
msgstr "inicia o hugin no modo de interface gráfica"
#: ../contrib/hugin.lua:237
msgid "hugin panorama"
msgstr "hugin panorama"

View File

@@ -0,0 +1,273 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 23:35-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/image_stack.lua:96
msgid "image align options"
msgstr "opções de alinhamento da imagem"
#: ../contrib/image_stack.lua:100
msgid "image stack options"
msgstr "opções de empilhamento da imagem"
#: ../contrib/image_stack.lua:104
msgid "executable locations"
msgstr "localizações dos executáveis"
#: ../contrib/image_stack.lua:129
msgid "perform image alignment"
msgstr "realizar o alinhamento da imagem"
#: ../contrib/image_stack.lua:131
msgid "align the image stack before processing"
msgstr "alinha a pilha de imagem antes de processar"
#: ../contrib/image_stack.lua:135
msgid "optimize radial distortion for all images"
msgstr "otimizar a distorção radial para todas as imagens"
#: ../contrib/image_stack.lua:137
msgid ""
"optimize radial distortion for all images, \n"
"except for first"
msgstr ""
"otimiza a distorção radial para todas as imagens,\n"
"exceto para a primeira"
#: ../contrib/image_stack.lua:141
msgid "optimize field of view for all images"
msgstr "otimizar campo de visão para todas as imagens"
#: ../contrib/image_stack.lua:143
msgid ""
"optimize field of view for all images, except for first. \n"
"Useful for aligning focus stacks (DFF) with slightly \n"
"different magnification."
msgstr ""
"otimiza o campo de visão para todas as imagens, exceto para a primeira.\n"
"Útil para alinhar o empilhamento de foco (DFF) com uma ampliação \n"
"levemente diferente."
#: ../contrib/image_stack.lua:147
msgid "optimize image center shift for all images"
msgstr "otimizar o deslocamento do centro da imagem para todas as imagens"
#: ../contrib/image_stack.lua:149
msgid ""
"optimize image center shift for all images, \n"
"except for first."
msgstr ""
"otimiza o deslocamento do centro da imagem para todas as imagens, \n"
"exceto para a primeira."
#: ../contrib/image_stack.lua:153
msgid "auto crop the image"
msgstr "cortar automaticamente a imagem"
#: ../contrib/image_stack.lua:155
msgid "auto crop the image to the area covered by all images."
msgstr ""
"corta automaticamente a imagem para a área coberta por todas as imagens."
#: ../contrib/image_stack.lua:159
msgid "load distortion from lens database"
msgstr "carregar a distorção a partir da base de dados de lentes"
#: ../contrib/image_stack.lua:161
msgid "try to load distortion information from lens database"
msgstr "tenta carregar as informações de distorção da base de dados de lentes"
#: ../contrib/image_stack.lua:165
msgid "image grid size"
msgstr "tamanho da grade da imagem"
#: ../contrib/image_stack.lua:166
msgid ""
"break image into a rectangular grid \n"
"and attempt to find num control points in each section.\n"
"default: (5x5)"
msgstr ""
"quebra a imagem em uma grade retangular \n"
"e tenta encontrar o número de pontos de controle\n"
"em cada seção.\n"
"padrão: (5x5)"
#: ../contrib/image_stack.lua:175
msgid "control points/grid"
msgstr "grade/pontos de controle"
#: ../contrib/image_stack.lua:176
msgid ""
"number of control points (per grid, see option -g) \n"
"to create between adjacent images \n"
"default: (8)."
msgstr ""
"número de pontos de controle (por grade, veja a opção -g) \n"
"a criar entre imagens adjacentes\n"
"padrão: (8)"
#: ../contrib/image_stack.lua:185
msgid "remove control points with error"
msgstr "remover pontos de controle com erro"
#: ../contrib/image_stack.lua:186
msgid ""
"remove all control points with an error higher \n"
"than num pixels \n"
"default: (3)"
msgstr ""
"remove todos os pontos de controle com um erro maior \n"
"que um número de pixels \n"
"padrão: (3)"
#: ../contrib/image_stack.lua:195
msgid "correlation threshold for control points"
msgstr "limiar de correlação para pontos de controle"
#: ../contrib/image_stack.lua:196
msgid ""
"correlation threshold for identifying \n"
"control points \n"
"default: (0.9)."
msgstr ""
"limiar de correlação para identificar pontos\n"
"de controle\n"
"padrão: (0,9)"
#: ../contrib/image_stack.lua:205
msgid "select stack function"
msgstr "função de seleção de pilha"
#: ../contrib/image_stack.lua:206
msgid ""
"select function to be \n"
"applied to image stack"
msgstr ""
"função de seleção a ser aplicada\n"
"à pilha de imagens"
#: ../contrib/image_stack.lua:215
msgid "select output format"
msgstr "selecionar formato de saída"
#: ../contrib/image_stack.lua:216
msgid "choose the format for the resulting image"
msgstr "seleciona o formato para a imagem resultante"
#: ../contrib/image_stack.lua:224
msgid "tag source images used?"
msgstr "etiquetas das imagens fonte usadas?"
#: ../contrib/image_stack.lua:226
msgid "tag the source images used to create the output file?"
msgstr "etiquetas das imagens fonte usadas para criar o arquivo de saída?"
#: ../contrib/image_stack.lua:261
#, lua-format
msgid "Export Image %i/%i"
msgstr "Exportar Imagem %i/%i"
#: ../contrib/image_stack.lua:454
msgid "Unrecognized option to copy_image_attributes: "
msgstr "Opção não reconhecida para copy_image_attributes: "
#: ../contrib/image_stack.lua:497
msgid "ERROR: at least 2 images required for image stacking, exiting..."
msgstr ""
"ERRO: pelo menos 2 imagens são necessárias para empilhamento de imagens,"
" saindo..."
#: ../contrib/image_stack.lua:498
msgid " image(s) selected, at least 2 required"
msgstr " imagem(ns) selecionada(s), pelo menos 2 necessárias"
#: ../contrib/image_stack.lua:512
msgid "aligning images..."
msgstr "alinhar imagens..."
#: ../contrib/image_stack.lua:515
msgid "images aligned"
msgstr "imagens alinhadas"
#: ../contrib/image_stack.lua:521
msgid "ERROR: image alignment failed"
msgstr "ERRO: alinhamento de imagem falhou"
#: ../contrib/image_stack.lua:522
msgid "image alignment failed"
msgstr "alinhamento de imagem falhou"
#: ../contrib/image_stack.lua:527
msgid "ERROR: align_image_stack not found"
msgstr "ERRO: align_image_stack não encontrado"
#: ../contrib/image_stack.lua:528
msgid "align_image_stack not found"
msgstr "align_image_stack não encontrado"
#: ../contrib/image_stack.lua:542
msgid "convert command is "
msgstr "comando convert é "
#: ../contrib/image_stack.lua:543
msgid "processing image stack"
msgstr "processar empilhamento de imagem"
#: ../contrib/image_stack.lua:546
msgid "image stack processed"
msgstr "empilhamento de imagem processado"
#: ../contrib/image_stack.lua:551
msgid "importing result"
msgstr "importar resultado"
#: ../contrib/image_stack.lua:556
msgid "Created with|image_stack"
msgstr "Criado com|image_stack"
#: ../contrib/image_stack.lua:569
msgid "tagging source images"
msgstr "etiquetando imagens fonte"
#: ../contrib/image_stack.lua:570
msgid "Source file|"
msgstr "Arquivo fonte |"
#: ../contrib/image_stack.lua:577
msgid "ERROR: image stack processing failed"
msgstr "ERRO: processamento de empilhamento de imagem falhou"
#: ../contrib/image_stack.lua:581
msgid "ERROR: convert executable not found"
msgstr "ERRO: executável do convert não encontrado"
#: ../contrib/image_stack.lua:582
msgid "convert executable not found"
msgstr "executável do convert não encontrado"
#: ../contrib/image_stack.lua:594
msgid "align image stack: use GPU for remaping"
msgstr "alinhar pilha de imagens: usar GPU para remapear"
#: ../contrib/image_stack.lua:595
msgid "set the GPU remapping for image align"
msgstr "definir o remapeamento de GPU para alinhamento de imagem"
#: ../contrib/image_stack.lua:598
msgid "image stack"
msgstr "empilhamento de imagem"

View File

@@ -0,0 +1,244 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-02 05:09-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/image_time.lua:177
msgid "Error: 2 images must be selected"
msgstr "Erro: 2 imagens devem ser selecionadas"
#: ../contrib/image_time.lua:230
msgid "unable to detect exiv2"
msgstr "não foi possível detectar exiv2"
#: ../contrib/image_time.lua:251 ../contrib/image_time.lua:268
msgid "unable to get information for "
msgstr "não foi possível obter informações para "
#: ../contrib/image_time.lua:316
msgid "reset time: no images selected"
msgstr "reiniciar tempo: nenhuma imagem selecionada"
#: ../contrib/image_time.lua:317
msgid "please select the images that need their time reset"
msgstr ""
"por favor, selecione as imagens que precisam que seu tempo seja reiniciado"
#: ../contrib/image_time.lua:330 ../contrib/image_time.lua:355
msgid "please select some images and try again"
msgstr "por favor, selecione algumas imagens e tente novamente"
#: ../contrib/image_time.lua:334 ../contrib/image_time.lua:437
#: ../contrib/image_time.lua:444
msgid "subtract"
msgstr "subtrair"
#: ../contrib/image_time.lua:407
msgid "image time"
msgstr "tempo da imagem"
#: ../contrib/image_time.lua:431
msgid "years"
msgstr "anos"
#: ../contrib/image_time.lua:431
msgid "years to adjust by, 0 - ?"
msgstr "anos a ajustar, 0 - ?"
#: ../contrib/image_time.lua:432
msgid "months"
msgstr "meses"
#: ../contrib/image_time.lua:433
msgid "days"
msgstr "dias"
#: ../contrib/image_time.lua:434
msgid "hours"
msgstr "horas"
#: ../contrib/image_time.lua:434
msgid "hours to adjust by, 0-23"
msgstr "horas a ajustar, 0-23"
#: ../contrib/image_time.lua:435
msgid "minutes"
msgstr "minutos"
#: ../contrib/image_time.lua:435
msgid "minutes to adjust by, 0-59"
msgstr "minutos a ajustar, 0-59"
#: ../contrib/image_time.lua:436 ../contrib/image_time.lua:443
msgid "seconds"
msgstr "segundos"
#: ../contrib/image_time.lua:436
msgid "seconds to adjust by, 0-59"
msgstr "segundos a ajustar, 0-59"
#: ../contrib/image_time.lua:437 ../contrib/image_time.lua:444
msgid "add/subtract"
msgstr "adicionar/subtrair"
#: ../contrib/image_time.lua:437 ../contrib/image_time.lua:444
msgid "add or subtract time"
msgstr "adicionar ou subtrair tempo"
#: ../contrib/image_time.lua:437 ../contrib/image_time.lua:444
msgid "add"
msgstr "adicionar"
#: ../contrib/image_time.lua:438
msgid "year"
msgstr "ano"
#: ../contrib/image_time.lua:438
msgid "year to set, 1900 - now"
msgstr "ano a definir, 1900 - agora"
#: ../contrib/image_time.lua:439
msgid "month"
msgstr "mês"
#: ../contrib/image_time.lua:439
msgid "month to set, 1-12"
msgstr "mês a definir, 1-12"
#: ../contrib/image_time.lua:440
msgid "day"
msgstr "dia"
#: ../contrib/image_time.lua:440
msgid "day to set, 1-31"
msgstr "dia a definir, 1-31"
#: ../contrib/image_time.lua:441
msgid "hour"
msgstr "hora"
#: ../contrib/image_time.lua:441
msgid "hour to set, 0-23"
msgstr "hora a definir, 0-23"
#: ../contrib/image_time.lua:442
msgid "minute"
msgstr "minuto"
#: ../contrib/image_time.lua:442
msgid "minutes to set, 0-59"
msgstr "minuto a definir, 0-59"
#: ../contrib/image_time.lua:443
msgid "seconds to set, 0-59"
msgstr "segundos a definir, 0-59"
#: ../contrib/image_time.lua:459
msgid "Time difference between images in seconds"
msgstr "Diferença de tempo entre as imagens em segundos"
#: ../contrib/image_time.lua:460
msgid "Select 2 images and use the calculate button"
msgstr "Selecione 2 imagens e use o botão para calcular"
#: ../contrib/image_time.lua:465
msgid "Calculate"
msgstr "Calcular"
#: ../contrib/image_time.lua:466
msgid "calculate time difference between 2 images"
msgstr "calcula a diferença de tempo entre 2 imagens"
#: ../contrib/image_time.lua:473
msgid "synchronize image times"
msgstr "sincronizar tempos da imagem"
#: ../contrib/image_time.lua:474
msgid "apply the time difference from selected images"
msgstr "aplica a diferença de tempo a partir das imagens selecionadas"
#: ../contrib/image_time.lua:484 ../contrib/image_time.lua:550
msgid "adjust time"
msgstr "ajustar tempo"
#: ../contrib/image_time.lua:485
msgid "days, months, years"
msgstr "dias, meses, anos"
#: ../contrib/image_time.lua:489
msgid "hours, minutes, seconds"
msgstr "horas, minutos, segundos"
#: ../contrib/image_time.lua:493
msgid "adjustment direction"
msgstr "direção de ajuste"
#: ../contrib/image_time.lua:496
msgid "adjust"
msgstr "ajustar"
#: ../contrib/image_time.lua:504 ../contrib/image_time.lua:551
msgid "set time"
msgstr "definir tempo"
#: ../contrib/image_time.lua:505
msgid "date: "
msgstr "data: "
#: ../contrib/image_time.lua:509
msgid "time:"
msgstr "tempo: "
#: ../contrib/image_time.lua:514
msgid "set"
msgstr "definir"
#: ../contrib/image_time.lua:522
msgid "synchronize image time"
msgstr "sincronizar tempo da imagem"
#: ../contrib/image_time.lua:523
msgid "calculate difference between images"
msgstr "calcular a diferença entre imagens"
#: ../contrib/image_time.lua:526
msgid "apply difference"
msgstr "aplicar diferença"
#: ../contrib/image_time.lua:532
msgid "reset to original time"
msgstr "reiniciar para o tempo original"
#: ../contrib/image_time.lua:535
msgid "reset"
msgstr "reiniciar"
#: ../contrib/image_time.lua:544
msgid "mode"
msgstr "modo"
#: ../contrib/image_time.lua:545
msgid "select mode"
msgstr "selecionar modo"
#: ../contrib/image_time.lua:552
msgid "synchronize time"
msgstr "sincronizar tempo"
#: ../contrib/image_time.lua:553
msgid "reset time"
msgstr "reiniciar tempo"

View File

@@ -0,0 +1,90 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-02 05:14-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/kml_export.lua:64
#, lua-format
msgid "Export Image %i/%i"
msgstr "Exportar Imagem %i/%i"
#: ../contrib/kml_export.lua:91
msgid "magick not found"
msgstr "magick não encontrado"
#: ../contrib/kml_export.lua:96
msgid "xdg-user-dir not found"
msgstr "xdg-user-dir não encontrado"
#: ../contrib/kml_export.lua:106
msgid "zip not found"
msgstr "zip não encontrado"
#: ../contrib/kml_export.lua:321
msgid "KML export: Open KML file after export"
msgstr "exportar KML: Abrir arquivo Open KML após a exportação"
#: ../contrib/kml_export.lua:322 ../contrib/kml_export.lua:329
msgid ""
"Opens the KML file after the export with the standard program for KML files"
msgstr ""
"Abre o arquivo KML após a exportação com o programa padrão para arquivos KML"
#: ../contrib/kml_export.lua:328
msgid "KML export: Open KML/KMZ file after export"
msgstr "exportar KML: Abre arquivo KML/KMZ após a exportação"
#: ../contrib/kml_export.lua:348
msgid "KML export: Export directory"
msgstr "exportar KML: Pasta de exportação"
#: ../contrib/kml_export.lua:349
msgid "A directory that will be used to export the KML/KMZ files"
msgstr "Uma pasta que será usada para exportar os arquivos KML/KMZ"
#: ../contrib/kml_export.lua:356
msgid "KML export: ImageMagick binary Location"
msgstr "exportar KML: Localização do binário do ImageMagick"
#: ../contrib/kml_export.lua:357
msgid "Install location of magick[.exe]. Requires restart to take effect."
msgstr ""
"Localização de instalação do magick[.exe]. Precisa reiniciar para ter efeito."
#: ../contrib/kml_export.lua:364
msgid "KML export: Connect images with path"
msgstr "exportar KML: Conectar imagens com caminho"
#: ../contrib/kml_export.lua:365
msgid "connect all images with a path"
msgstr "conecta todas as imagens com um caminho"
#: ../contrib/kml_export.lua:372
msgid "KML export: Create KMZ file"
msgstr "exportar KML: Criar arquivo KMZ"
#: ../contrib/kml_export.lua:373
msgid "Compress all imeges to one KMZ file"
msgstr "Comprime todas as imagens para um arquivo KMZ"
#: ../contrib/kml_export.lua:379
msgid "KML Export"
msgstr "Exportar KML"
#: ../contrib/kml_export.lua:381
msgid "KML/KMZ Export"
msgstr "Exportar KML/KMZ"

View File

@@ -0,0 +1,22 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Marcus Gama <marcus.gama@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-28 19:37-0300\n"
"PO-Revision-Date: 2021-10-01 19:26-0300\n"
"Last-Translator: Marcus Gama <marcus.gama@gmail.com>\n"
"Language-Team: Portuguese <darktable-dev@lists.darktable.org>\n"
"Language: pt_BR\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 21.08.1\n"
#: ../contrib/passport_guide.lua:90
msgid "ISO 19794-5/ICAO 9309 passport"
msgstr "passaporte ISO 19794-5/ICAO 9309"

Some files were not shown because too many files have changed in this diff Show More