307 Commits

Author SHA1 Message Date
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
wpferguson
8afd6ccee4 Merge pull request #202 from wpferguson/fix_libraries
fix libraries
2019-12-16 22:45:59 -05:00
Bill Ferguson
e427294ec1 fix library problems found during unit testing 2019-12-16 22:42:32 -05:00
wpferguson
18283ccbc6 Merge pull request #201 from darktable-org/revert-199-library_fixes
Revert "Library fixes"
2019-12-16 22:31:40 -05:00
wpferguson
b7857c8462 Revert "Library fixes" 2019-12-16 22:30:50 -05:00
wpferguson
2d231806d2 Merge pull request #199 from wpferguson/library_fixes
Library fixes
2019-12-16 21:46:14 -05:00
wpferguson
36b58333ca Merge pull request #200 from wpferguson/README_update
Update README.md
2019-12-16 21:40:26 -05:00
Bill Ferguson
fc77af598d Another update 2019-12-16 20:39:10 -05:00
Bill Ferguson
602295ae89 Updated contributed scripts 2019-12-16 20:27:20 -05:00
Bill Ferguson
1c295a88ac Fixed bugs found during unit testing with testing framework 2019-12-11 22:55:44 -05:00
wpferguson
232535faf9 Merge pull request #197 from wpferguson/update_face_recognition
Update face recognition
2019-12-09 20:19:24 -05:00
Bill Ferguson
10b5dee40f Added windows compatibility 2019-12-09 20:08:11 -05:00
Bill Ferguson
be48459092 Converted face_recognition to a module and updated it to use newer library functions. Added tolerance adjustment to settings to fine tune the face detection. 2019-12-08 20:18:07 -05:00
supertobi
cb6199c1a8 Merge pull request #100 from GHswitt/master
Storage plugin for face_recognition
2019-12-07 21:34:38 +01:00
supertobi
c9eb087ade Update README.md
The ' is not not needed in the windows cmd
2019-12-04 13:01:58 +01:00
supertobi
87e223a854 Update README.md
Added: If you don't have %LOCALAPPDATA%\darktable you have to start dartable at least once, because the directory is created at the first start of darktable.
2019-12-04 12:52:44 +01:00
wpferguson
7c7118dfad Merge pull request #193 from simon04/patch-1
README: list various third-party projects
2019-11-10 00:33:39 -05:00
Simon Legner
fdcd6c6dfe README: list various third-party projects 2019-11-04 21:42:15 +01:00
Bill Ferguson
205cbcf05d Checkpoint save 2019-07-03 12:41:58 -04:00
wpferguson
c9ccbb2750 Merge pull request #189 from wpferguson/remove_plugin_manager_symlink
Removed symlink for plugin_manager development
2019-06-19 11:25:32 -04:00
Bill Ferguson
eacfc4b623 Removed symlink for plugin_manager development 2019-06-19 11:23:11 -04:00
supertobi
d3dafd4ce1 Merge pull request #179 from BzKevin/BzKevin-enfuseAdvanced-refactor
Refactored enfuseAdvanced
2019-05-07 15:56:06 +02:00
supertobi
8004b96c41 Merge branch 'master' into BzKevin-enfuseAdvanced-refactor 2019-05-07 15:54:12 +02:00
supertobi
7cc58d9fac Merge pull request #164 from BzKevin/BzKevin-HDRMerge
Add HDRMerge
2019-05-07 13:00:40 +02:00
BzKevin
f82ae6c091 Change 'Create Unique Filename' to default 2019-05-06 21:35:41 -04:00
BzKevin
fcad7a5733 Makte Translatable + others
Made text translatable except for line 499 which kept giving me problems
   Making L499 translatable would throw the following error   lua:499: attempt to call a nil value (upvalue '_')
   Troubleshooting steps taken and results:
        1) Changed the translation function from _() to translate() - this worked, no errors but ugly
        2) Reverted the changes from 1, then changed all "for _, var in table do" to be "for ignore, var in table do" - this did not help

Moved a chunk of code due to anomalous behavior, I suspect previous code was attempting to change the value in the stack widget before it was fully created. Adding in a print statement before trying to change the stack.active would cause things to work fine. Moving it behind the register call also helped.
2019-03-22 12:01:56 -04:00
BzKevin
7ffdf1631c Make text translatable 2019-03-22 09:05:11 -04:00
BzKevin
a5ad5e67f3 Remove executable preferences
The executable choosers have been moved to a box which is now element 4 in the GUI stack. Once the user configures these and presses update they are confirmed good or bad and the GUI changed to normal view if all works out.
2019-03-12 13:04:21 -04:00
BzKevin
4b3b1f7786 Fix bug when updating exe preference
Move the preference.write() call inside the for loop
2019-03-12 12:42:37 -04:00
BzKevin
844e89a652 Use file chooser to set executable path preference
previous commit failed to actually utilize the newly created file chooser to set the path preference
2019-03-12 12:25:37 -04:00
BzKevin
d88af9ffd9 Minor tweaks to last commit 2019-03-12 11:20:04 -04:00
BzKevin
7a3a9ba559 Removed preferences for exe
The GUI is now made of a stack which has two boxes in it. One box contains all the normal GUI elements, the other contains a file chooser button and a normal button to call an update routine. This box is only displayed if an issue is found. I decided to track the install status via a preference boolean, rather than calling df_check_if_bin_exists at startup every time. I do this because on my windows PC I was starting to get slow startups with too many scripts installed and many many flashing CMD windows at startup as well, which was a nuisance.
2019-03-12 11:19:22 -04:00
BzKevin
5d3910da49 Tab to Space 2019-03-05 09:14:28 -05:00
BzKevin
f740d34f1f Tab to Space 2019-03-05 09:12:54 -05:00
BzKevin
58ca70c511 Tab to Space 2019-03-05 09:11:28 -05:00
BzKevin
ee59540648 Refactored
I was having issue wit this script periodically. The original version of this was real a hack-job of Holger Klemms original. This new version utilizes tables in a manner that I hope will make the code easier to follow and understand, but also allows for certain functions to be re-used throughout (and potentially in different scripts as well). In general:

Each program (align image stack, enfuse, exiftool) how has a table that contains information about that program, this includes a sub-table that contains argument data for the program.
There is a GUI table which contains all gui elements. Those elements are split into sub-tables within GUI based on what aspect of the scrpt behavior they relate to (AIS, ENF, Target File, Presets, etc...)
These two structures (and the similarities between them) have allows me to create loops which populate the arguments from the GUI and vice-versa, this is all done through an intermediary preference, the "active" preference. 
Whenever a change to a GUI element occurs the associated member of hte active preference is updated. then when it becomes time to build an argument string for a program command the active preference is read from and the value contained within is used. This works fine for all GUI elements that have a "changed" or "clicked" callback, as they can remain up-to-date real time. Sliders and Entry boxes do not have this, so at a certain point withing the script the GUI values are read directly for these elements and written to the associated active preference field.

As you will see about 50% of this code is simply building all the GUI elements and making sure that the initialization value for that element is valid (via the InRange() function). The actual heart of the program and it's functions really only takes up about 300 lines (starting at 180 and going through 476). That is broken down into multiple fairly concise functions. Hopefully this makes maintenance, feature enhancement, and troubleshooting much easier in the future.

I also made a few UI tweaks. This script use to create such a massive list of GUI elements that it was a nuisance when actually using it. They are now grouped into three containers for AIS options, Enfuse options, and target options. Only one of these options containers is displayed at a time. Additionally, I removed all the registered preferences I had before, so now I only register 3 options to enable user to point towards the program binaries.
2019-03-04 13:13:49 -05:00
BzKevin
85c242ff0e Comments added, minor updates
I have added comments throughout, mainly to the functions
I improved the performance of the CleanSpaces() function
Improved communication to user via dt.print()
2019-03-04 09:45:05 -05:00
BzKevin
7bc4846c0a Refactored
After experiencing some issues after upgrading to dt2.6 I decided it would be worthwhile to refactor the code for more reliable performance. Specifically the script would crash dt when attempting to apply a style on import and sometimes just fail to run. I have refactored this in a manor that should make it more logical for future users to add/maintain as applicable. 

Refactored script has only been tested on Windows 10 w/ dt2.6. Hopefullt all cross-platform aspects have been maintained.
2019-03-03 15:48:08 -05:00
BzKevin
5fd3a56a31 Remove extraneous widgets in preferences
I now understand how preferences work better and have removed the extraneous widgets which were doing nothing
2019-02-15 14:24:51 -05:00
BzKevin
2cb3397d75 Update HDRMerge
Now only checks for binary proper install once the functionality of the program is actually utilized. This avoids to many external command calls at dt startup just to check if certain things are installed.
2019-02-15 13:22:49 -05:00
BzKevin
2a2fbd18cd Add HDRMerge 2019-02-13 12:21:57 -05:00
Simon Legner
11607e7e7d Delete compiled .mo files 2018-09-18 10:26:40 +02:00
GHswitt
8e6552ee9c Added ignore tag and unknown tag, verbose output 2017-12-11 17:43:53 +01:00
GHswitt
024e016df2 Storage plugin for face_recognition 2017-12-11 14:27:36 +01:00
65 changed files with 7992 additions and 2436 deletions

72
ChangeLog.md Normal file
View File

@@ -0,0 +1,72 @@
## Changes from most recent to oldest
** 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

193
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
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
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
----|:--------:|:---:|-------
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
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
enfuse|No|L|Exposure blend several images (HDR)
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
import_filter_manager|Yes|LMW|Manager for import filters
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
selection_to_pdf|No|L|Generate a PDF file from the selected images
[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](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](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](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/delete_unused_tags)|Yes|LMW|Delete tags that have no associated images
[enfuse](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/enfuse)|No|L|Exposure blend several images (HDR)
[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](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](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/import_filter_manager)|Yes|LMW|Manager for import filters
[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](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](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
@@ -32,28 +34,40 @@ These scripts are contributed by users. They are meant to have an "owner", i.e.
Name|Standalone|OS |Purpose
----|:--------:|:---:|-------
autostyle|Yes|LMW|Automatically apply styles on import
clear_GPS|Yes|LMW|Reset GPS information for selected images
copy_attach_detach_tags|Yes|LMW|Copy and paste tags from/to images
cr2hdr|Yes|L|Process image created with Magic Lantern Dual ISO
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
geoJSON_export|No|L|Create a geo JSON script with thumbnails for use in ...
geoToolbox|No|LMW|A toolbox of geo functions
gimp|No|LMW|Open an image in GIMP for editing and return the result
gpx_export|No|LMW|Export a GPX track file from selected images GPS data
hugin|No|LMW|Combine selected images into a panorama and return the result
image_stack|No|LMW|Combine a stack of images to remove noise or transient objects
kml_export|No|L|Export photos with a KML file for usage in Google Earth
LabelsToTags|Yes|LMW|Apply tags based on color labels and ratings
passport_guide|Yes|LMW|Add passport cropping guide to darkroom crop tool
pdf_slideshow|No|LM|Export images to a PDF slideshow
quicktag|Yes|LMW|Create shortcuts for quickly applying tags
rate_group|Yes|LMW|Apply or remove a star rating from grouped images
rename-tags|Yes|LMW|Change a tag name
select_untagged|Yes|LMW|Enable selection of untagged images
slideshowMusic|No|L|Play music during a slideshow
video_mencoder|No|L|Export video from darktable
[AutoGrouper](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/AutoGrouper)|Yes|LMW|Group images together by time
[autostyle](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/autostyle)|Yes|LMW|Automatically apply styles on import
[clear_GPS](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/clear_GPS)|Yes|LMW|Reset GPS information for selected 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
[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
[cr2hdr](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/cr2hdr)|Yes|L|Process image created with Magic Lantern Dual ISO
[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
[exportLUT](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/exportLUT)|Yes|LMW|Create a LUT from a style and export it
[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
[face_recognition](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/face_recognition)|No|LM|Identify and tag images using facial recognition
[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
[fujifilm_ratings](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/fujifilm_ratings)|No|LM|Support importing Fujifilm ratings
[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 ...
[geoToolbox](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/geoToolbox)|No|LMW|A toolbox of geo functions
[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
[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
[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
[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
[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
[image_time](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/image_time)|Yes|LMW|Adjust the EXIF image time
[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
[LabelsToTags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/LabelsToTags)|Yes|LMW|Apply tags based on color labels and ratings
[OpenInExplorer](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/OpenInExplorer)|No|LMW|Open the selected images in the system file manager
[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
[pdf_slideshow](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/pdf_slideshow)|No|LM|Export images to a PDF slideshow
[photils](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/photils)|No|LM|Automatic tag suggestions for your images
[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-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_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
@@ -61,14 +75,17 @@ These scripts provide examples of how to use specific portions of the API. They
Name|Standalone|OS |Purpose
----|:--------:|:---:|-------
api_version|Yes|LMW|Print the current API version
gettextExample|Yes|LM|How to use translation
hello_world|Yes|LMW|Prints hello world when darktable starts
moduleExample|Yes|LMW|How to create a lighttable module
multi_os|No|LMW|How to create a cross platform script that calls an external executable
preferenceExamples|Yes|LMW|How to use preferences in a script
printExamples|Yes|LMW|How to use various print functions from a script
running_os|Yes|LMW|Print out the running operating system
[api_version](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/api_version)|Yes|LMW|Print the current API version
[darkroom_demo](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/darkroom_demo)|Yes|LMW|Demonstrate changing images in darkoom
[gettextExample](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/gettextExample)|Yes|LM|How to use translation
[hello_world](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/hello_world)|Yes|LMW|Prints hello world when darktable starts
[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
[moduleExample](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/moduleExample)|Yes|LMW|How to create a lighttable module
[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
[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
@@ -76,11 +93,27 @@ Tool scripts perform functions relating to the repository, such as generating do
Name|Standalone|OS |Purpose
----|:--------:|:---:|-------
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)
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
script_manager|No|LMW|Manage (install, update, enable, disable) 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](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](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](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](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
The following third-party projects are listed for information only. Think of this collection as an `awesome-darktable-lua-scripts` list. Use at your own risk!
* [trougnouf/dtMediaWiki](https://github.com/trougnouf/dtMediaWiki) – Wikimedia Commons export
* [wpferguson/extra-dt-lua-scripts](https://github.com/wpferguson/extra-dt-lua-scripts)
* [xxv/darktable-git-annex](https://github.com/xxv/darktable-git-annex) – git-annex integration
* [theres/dt_backup](https://github.com/theres/dt_backup) – automatic backup on exit
* [Sitwon/dt_fujifilm_ratings](https://github.com/Sitwon/dt_fujifilm_ratings) – Fujifilm Ratings import
* [progo/leica-q-autocrop](https://github.com/progo/leica-q-autocrop) – crop in 35 mm or 50 mm equivalent
* [BzKevin/OpenInPS-Darktable-PlugIn](https://github.com/BzKevin/OpenInPS-Darktable-PlugIn) – open in Adobe Photoshop
* [johnnyrun/darktable_lua_gimp](https://github.com/johnnyrun/darktable_lua_gimp) – GIMP export
* [arru/darktable-scripts](https://github.com/arru/darktable-scripts)
* [nbremond77/darktable](https://github.com/nbremond77/darktable/tree/master/scripts)
* [s5k6/dtscripts](https://github.com/s5k6/dtscripts)
## Download and Install
@@ -89,14 +122,24 @@ are met as well as providing an easy update path. Single scripts listed as stand
### 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:
cd ~/snap/darktable/current
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.
@@ -111,15 +154,33 @@ 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:
Open a command prompt.
cd %LOCALAPPDATA%\darktable
git clone https://github.com/darktable-org/lua-scripts.git lua
If you don't have %LOCALAPPDATA%\darktable you have to start dartable at least once, because the directory is created at the first start of darktable.
## Enabling
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:
`echo 'require "contrib/gimp"' > ~/.config/darktable/luarc` to create the file with a gimp entry\
@@ -127,8 +188,8 @@ or `echo 'require "contrib/hugin"' >> ~/.config/darktable/luarc` to add an entry
On windows from a command prompt:
`echo 'require "contrib/gimp"' > %LOCALAPPDATA%\darktable\luarc` to create the file with a gimp entry\
or `echo 'require "contrib/hugin"' >> %LOCALAPPDATA%\darktable\luarc` to add an entry for hugin.
`echo require "contrib/gimp" > %LOCALAPPDATA%\darktable\luarc` to create the file with a gimp entry\
or `echo require "contrib/hugin" >> %LOCALAPPDATA%\darktable\luarc` to add an entry for hugin.
## Disabling
@@ -143,6 +204,12 @@ To update the script repository, open a terminal or command prompt and do the fo
cd ~/snap/darktable/current/lua
git pull
### Flatpak
cd ~/.var/app/org.darktable.Darktable/config/darktable/lua
git pull
### Linux and MacOS
cd ~/.config/darktable/lua/
@@ -155,15 +222,19 @@ To update the script repository, open a terminal or command prompt and do the fo
## 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:
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:
https://www.darktable.org/lua-api/
The [Lua API Manual](https://darktable-org.github.io/luadocs/lua.api.manual/) provides docuemntation of the
darktable Lua API.
## Troubleshooting
@@ -173,13 +244,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.
### 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.
### 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
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

View File

@@ -37,14 +37,27 @@ selected images, the other button performs grouping on the entire active collect
]]
local dt = require "darktable"
local MOD = 'autogrouper'
local gettext = dt.gettext
-- Tell gettext where to find the .mo file translating messages for a particular domain
local CURR_API_STRING = dt.configuration_api_version_string
gettext.bindtextdomain("AutoGrouper",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("AutoGrouper", msgid)
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)
if test >= low and test <= high then
return true
@@ -111,12 +124,26 @@ local function main(on_collection)
end
end
local function install_module()
if not Ag.module_installed then
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",
GUI.gap,
GUI.selected,
GUI.collection
}
)
Ag.module_installed = true
end
end
-- GUI --
GUI = {
gap = {},
selected = {},
collection = {}
}
temp = dt.preferences.read(MOD, 'active_gap', 'integer')
if not InRange(temp, 1, 86400) then temp = 3 end
GUI.gap = dt.new_widget('slider'){
@@ -143,16 +170,20 @@ GUI.collection = dt.new_widget("button"){
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",
GUI.gap,
GUI.selected,
GUI.collection
}
)
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not Ag.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
)
Ag.event_registered = true
end
end

View File

@@ -53,6 +53,8 @@ local all_active = false
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("CollectHelper",dt.configuration.config_dir.."/lua/locale/")
local CURR_API_STRING = dt.configuration.api_version_string
local function _(msgid)
return gettext.dgettext("CollectHelper", msgid)
end

451
contrib/HDRMerge.lua Normal file
View File

@@ -0,0 +1,451 @@
--[[HDRMerge plugin for darktable
copyright (c) 2018 Kevin Ertel
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/>.
]]
--[[About this Plugin
This plugin adds the module 'HDRMerge' to darktable's lighttable view
----REQUIRED SOFTWARE----
HDRMerge ver. 4.5 or greater
----USAGE----
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.
2) Require this file in your luarc file, as with any other dt plug-in
On the initial startup go to darktable settings > lua options and set your executable paths and other preferences, then restart darktable
Select bracketed images and press the Run HDRMerge button. The resulting DNG will be auto-imported into darktable.
Additional tags or style can be applied on auto import as well, if you desire.
Base Options:
Select your desired BPS (bits per sample and Embedded Preview Size.
Batch Options:
Select if you want to run in batch mode or not
Select the gap, in seconds, between images for auto grouping in batch mode
See HDRMerge manual for further detail: http://jcelaya.github.io/hdrmerge/documentation/2014/07/11/user-manual.html
Auto-import Options:
Select a style, whether you want tags to be copied from the original, and any additional tags you desire added when the new image is auto-imported
]]
local dt = require 'darktable'
local df = require 'lib/dtutils.file'
local dsys = require 'lib/dtutils.system'
local mod = 'module_HDRMerge'
local os_path_seperator = '/'
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
local gettext = dt.gettext
gettext.bindtextdomain('HDRMerge', dt.configuration.config_dir..'/lua/locale/')
local function _(msgid)
return gettext.dgettext('HDRMerge', msgid)
end
local temp
local HDRM = { --HDRMerge Program Table
name = 'HDRMerge',
bin = '',
first_run = true,
install_error = false,
arg_string = '',
images_string = '',
args = {
bps = {text = '-b ', style = 'integer'},
size = {text = '-p ', style = 'string'},
batch = {text = '-B ', style = 'bool'},
gap = {text = '-g ', style = 'integer'}
}
}
local GUI = { --GUI Elements Table
HDR = {
bps ={},
size ={},
batch ={},
gap ={}
},
Target = {
style ={},
copy_tags ={},
add_tags ={}
},
run = {},
stack = {},
options = {},
exes = {
HDRMerge = {},
update = {},
}
}
HDRM.module_installed = false
HDRM.event_registered = false
--Detect User Styles--
local styles = dt.styles
local styles_count = 1 -- 'none' = 1
for _,i in pairs(dt.styles) do
if type(i) == 'userdata' then styles_count = styles_count + 1 end
end
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
return true
else
return false
end
end
local function GetFileName(full_path) --Parses a full path (path/filename_identifier.extension) into individual parts
--[[Input: Folder1/Folder2/Folder3/Img_0001.CR2
Returns:
path: Folder1/Folder2/Folder3/
filename: Img_0001
identifier: 0001
extension: .CR2
EX:
path_1, file_1, id_1, ext_1 = GetFileName(full_path_1)
]]
local path = string.match(full_path, '.*[\\/]')
local filename = string.gsub(string.match(full_path, '[%w-_]*%.') , '%.' , '' )
local identifier = string.match(filename, '%d*$')
local extension = string.match(full_path, '%.%w*')
return path, filename, identifier, extension
end
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*$','')
return text
end
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)
return result
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
for _,prog in pairs(prog_tbl) do
if prog.first_run then
prog.bin = df.check_if_bin_exists(prog.name)
if not prog.bin then
prog.install_error = true
dt.preferences.write(mod, 'bin_exists', 'bool', false)
else
prog.bin = CleanSpaces(prog.bin)
end
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 location'))
end
end
local function ExeUpdate(prog_tbl)
dt.preferences.write(mod, 'bin_exists', 'bool', true)
for _,prog in pairs(prog_tbl) do
dt.preferences.write('executable_paths', prog.name, 'string', GUI.exes[prog.name].value)
prog.bin = df.check_if_bin_exists(prog.name)
if not prog.bin then
prog.install_error = true
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
dt.print(_('update unsuccessful, please try again'))
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
temp = GUI.HDR.gap.value
dt.preferences.write(mod, 'active_gap', 'integer', temp)
temp = GUI.Target.add_tags.text
dt.preferences.write(mod, 'active_add_tags', 'string', temp)
end
local function main()
PreCall({HDRM}) --check if furst run then check if install OK
if HDRM.install_error then
dt.print_error(_('HDRMerge install issue'))
dt.print(_('HDRMerge install issue, please ensure the binary path is proper'))
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
if source_id > largest_id then largest_id = source_id end
end
out_path = out_path..os_path_seperator..smallest_name..'-'..largest_id..'.dng'
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
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
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
-- GUI Elements --
local lbl_hdr = dt.new_widget('section_label'){
label = _('HDRMerge options')
}
temp = dt.preferences.read(mod, 'active_bps_ind', 'integer')
if not InRange(temp, 1, 3) then temp = 3 end
GUI.HDR.bps = dt.new_widget('combobox'){
label = _('bits per sample'),
tooltip =_('number of bits per sample in the output image'),
selected = temp,
'16','24','32',
changed_callback = function(self)
dt.preferences.write(mod, 'active_bps', 'integer', self.value)
dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected)
end,
reset_callback = function(self)
self.selected = 3
dt.preferences.write(mod, 'active_bps', 'integer', self.value)
dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected)
end
}
temp = dt.preferences.read(mod, 'active_size_ind', 'integer')
if not InRange(temp, 1, 3) then temp = 2 end
GUI.HDR.size = dt.new_widget('combobox'){
label = _('embedded preview size'),
tooltip =_('size of the embedded preview in output image'),
selected = temp,
_('none'),_('half'),_('full'),
changed_callback = function(self)
dt.preferences.write(mod, 'active_size', 'string', self.value)
dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected)
end,
reset_callback = function(self)
self.selected = 2
dt.preferences.write(mod, 'active_size', 'string', self.value)
dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected)
end
}
GUI.HDR.batch = dt.new_widget('check_button'){
label = _('batch mode'),
value = dt.preferences.read(mod, 'active_batch', 'bool'),
tooltip = _('enable batch mode operation \nNOTE: resultant files will NOT be auto-imported'),
clicked_callback = function(self)
dt.preferences.write(mod, 'active_batch', 'bool', self.value)
GUI.HDR.gap.sensitive = self.value
end,
reset_callback = function(self) self.value = false end
}
temp = dt.preferences.read(mod, 'active_gap', 'integer')
if not InRange(temp, 1, 3600) then temp = 3 end
GUI.HDR.gap = dt.new_widget('slider'){
label = _('batch gap [sec.]'),
tooltip = _('gap, in seconds, between batch mode groups'),
soft_min = 1,
soft_max = 30,
hard_min = 1,
hard_max = 3600,
step = 1,
digits = 0,
value = temp,
sensitive = GUI.HDR.batch.value,
reset_callback = function(self)
self.value = 3
end
}
local lbl_import = dt.new_widget('section_label'){
label = _('import options')
}
GUI.Target.style = dt.new_widget('combobox'){
label = _('apply style on import'),
tooltip = _('Apply selected style on auto-import to newly created image'),
selected = 1,
_('none'),
changed_callback = function(self)
dt.preferences.write(mod, 'active_style', 'string', self.value)
dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected)
end,
reset_callback = function(self)
self.selected = 1
dt.preferences.write(mod, 'active_style', 'string', self.value)
dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected)
end
}
for k=1, (styles_count-1) do
GUI.Target.style[k+1] = styles[k].name
end
temp = dt.preferences.read(mod, 'active_style_ind', 'integer')
if not InRange(temp, 1, styles_count) then temp = 1 end
GUI.Target.style.selected = temp
GUI.Target.copy_tags = dt.new_widget('check_button'){
label = _('copy tags'),
value = dt.preferences.read(mod, 'active_copy_tags', 'bool'),
tooltip = _('copy tags from first source image'),
clicked_callback = function(self) dt.preferences.write(mod, 'active_copy_tags', 'bool', self.value) end,
reset_callback = function(self) self.value = true end
}
temp = dt.preferences.read(mod, 'active_add_tags', 'string')
if temp == '' then temp = nil end
GUI.Target.add_tags = dt.new_widget('entry'){
tooltip = _('Additional tags to be added on import. Seperate with commas, all spaces will be removed'),
text = temp,
placeholder = _('Enter tags, seperated by commas'),
editable = true
}
GUI.run = dt.new_widget('button'){
label = _('merge'),
tooltip =_('run HDRMerge with the above specified settings'),
clicked_callback = function() main() end
}
GUI.exes.HDRMerge = dt.new_widget('file_chooser_button'){
title = _('Select HDRmerge executable'),
value = df.get_executable_path_preference(HDRM.name),
is_directory = false
}
GUI.exes.update = dt.new_widget('button'){
label = _('update'),
tooltip =_('update the binary path with current value'),
clicked_callback = function() ExeUpdate({HDRM}) end
}
GUI.options = dt.new_widget('box'){
orientation = 'vertical',
lbl_hdr,
GUI.HDR.bps,
GUI.HDR.size,
GUI.HDR.batch,
GUI.HDR.gap,
lbl_import,
GUI.Target.style,
GUI.Target.copy_tags,
GUI.Target.add_tags,
GUI.run
}
local exes_box = dt.new_widget('box'){
orientation = 'vertical',
GUI.exes.HDRMerge,
GUI.exes.update
}
GUI.stack = dt.new_widget('stack'){
GUI.options,
exes_box
}
if dt.preferences.read(mod, 'bin_exists', 'bool') then
GUI.stack.active = 1
else
GUI.stack.active = 2
end
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not HDRM.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
)
HDRM.event_registered = true
end
end

View File

@@ -55,7 +55,12 @@ du.check_min_api_version("3.0.0", "LabelsToTags")
-- Lua 5.3 no longer has "unpack" but "table.unpack"
unpack = unpack or table.unpack
local ltt = {}
ltt.module_installed = false
ltt.event_registered = false
local LIB_ID = "LabelsToTags"
local CURR_API_STRING = darktable.configuration.api_version_string
-- Helper functions: BEGIN
@@ -184,7 +189,7 @@ local function doTagging(selfC)
job.valid = false
end
local my_widget = darktable.new_widget("box") {
ltt.my_widget = darktable.new_widget("box") {
orientation = "vertical",
mappingComboBox,
darktable.new_widget("button") {
@@ -217,6 +222,15 @@ darktable.register_tag_mapping = function(name, mapping)
mappingComboBox.reset_callback(mappingComboBox)
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
--[[
darktable.register_tag_mapping("Example",
{ ["+----*"] = { "Red", "Only red" },
@@ -229,6 +243,19 @@ darktable.register_tag_mapping("Example",
["*****R"] = { "Rejected" } })
]]
darktable.register_lib(LIB_ID,"labels to tags",true,true,{
[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",20},
},my_widget,nil,nil)
if darktable.gui.current_view().id == "lighttable" then
install_module()
else
if not ltt.event_registered then
darktable.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
)
ltt.event_registered = true
end
end

View File

@@ -2,6 +2,8 @@
OpenInExplorer plugin for darktable
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
it under the terms of the GNU General Public License as published by
@@ -18,18 +20,29 @@ OpenInExplorer plugin for darktable
]]
--[[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----
Microsoft Windows or Linux with installed Nautilus
Apple macOS, Microsoft Windows or Linux
----USAGE----
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
Select the photo(s) you wish to find in explorer and press "Go to Folder".
A file explorer window will be opened for each selected file at the file's location; the file will be highlighted.
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.
- 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----
]]
@@ -38,76 +51,167 @@ local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local dsys = require "lib/dtutils.system"
local gettext = dt.gettext
--Check API version
du.check_min_api_version("5.0.0", "OpenInExplorer")
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("OpenInExplorer",dt.configuration.config_dir.."/lua/locale/")
--Detect OS and modify accordingly--
local proper_install = false
if dt.configuration.running_os ~= "macos" then
proper_install = true
else
dt.print_error('OpenInExplorer plug-in only supports Windows and Linux at this time')
local function _(msgid)
return gettext.dgettext("OpenInExplorer", msgid)
end
local act_os = dt.configuration.running_os
local PS = act_os == "windows" and "\\" or "/"
local CURR_API_STRING = dt.configuration.api_version_string
--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
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
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 = "explorer.exe /select, "..curr_image
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
resp = dsys.external_command(run_cmd)
--Format strings for the commands to open the corresponding OS's file manager.
local open_dir = {}
open_dir.windows = "explorer.exe /n, %s"
open_dir.macos = "open %s"
open_dir.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowFolders ass 1 %s ""]]
local open_files = {}
open_files.windows = "explorer.exe /select, %s"
open_files.macos = "open -Rn %s"
open_files.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowItems ass %d %s ""]]
--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
else
dt.print('please select fewer images (max 15)')
end
end
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)
]]
if dsys.external_command(run_cmd) ~= 0 then
dt.print(_("Failed to create links. Missing rights?"))
dt.print_error("OpenInExplorer: Failed to create links")
return
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
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
local function open_in_filemanager() --Open
--Inits--
if not proper_install then
return
end
if (dt.configuration.running_os == "windows") then
open_in_explorer()
elseif (dt.configuration.running_os == "linux") then
open_in_nautilus()
end
end
-- GUI --
if proper_install then
dt.gui.libs.image.register_action(
"show in file explorer",
function() open_in_filemanager() end,
"Opens File Explorer at the selected image's location"
dt.gui.libs.image.register_action(
_("show in file explorer"),
function() open_in_fmanager() end,
_("Open the file manager 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
dt.register_event(
"shortcut",
function(event, shortcut) open_in_fmanager() end,
"OpenInExplorer"
)

230
contrib/RL_out_sharp.lua Normal file
View File

@@ -0,0 +1,230 @@
--[[
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("5.0.0", MODULE_NAME)
-- 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
-- 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")
-- end of script --------------------------------------------------------------
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua;

View File

@@ -42,6 +42,7 @@ local filelib = require "lib/dtutils.file"
-- Forward declare the functions
local autostyle_apply_one_image,autostyle_apply_one_image_event,autostyle_apply,exiftool_attribute,capture
local CURR_API_STRING = darktable.configuration.api_version_string
-- Tested it with darktable 1.6.1 and darktable git from 2014-01-25
du.check_min_api_version("2.0.2", "autostyle")
@@ -151,11 +152,12 @@ function get_stdout(cmd)
end
-- Registering events
darktable.register_event("shortcut",autostyle_apply,
darktable.register_event("shortcut", autostyle_apply,
"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.register_event("post-import-image",autostyle_apply_one_image_event)
darktable.register_event("post-import-image",
autostyle_apply_one_image_event)

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>.
@@ -38,12 +38,14 @@
local dt = require "darktable"
local du = require "lib/dtutils"
local gettext = dt.gettext
-- not a number
local NaN = 0/0
du.check_min_api_version("3.0.0", "clear_GPS")
local CURR_API_STRING = dt.configuration.api_version_string
-- Tell gettext where to find the .mo file translating messages for a particular domain
@@ -62,15 +64,14 @@ local function clear_GPS(images)
end
end
dt.gui.libs.image.register_action(
_("clear GPS data"),
function(event, images) clear_GPS(images) end,
"clear GPS data"
_("Clear GPS data from selected images")
)
dt.register_event(
"shortcut",
function(event, shortcut) clear_GPS(dt.gui.action_images) end,
"clear GPS data"
_("Clear GPS data")
)

View File

@@ -43,6 +43,7 @@ local debug = require "darktable.debug"
local gettext = dt.gettext
du.check_min_api_version("3.0.0", "copy_attach_detach_tags")
local CURR_API_STRING = dt.configuration.api_version_string
-- 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/")
@@ -51,6 +52,11 @@ local function _(msgid)
return gettext.dgettext("copy_attach_detach_tags", msgid)
end
local cadt = {}
cadt.module_installed = false
cadt.event_registered = false
cadt.widget_table = {}
local image_tags = {}
@@ -107,7 +113,7 @@ local function mcopy_tags()
local function attach_tags()
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
end
@@ -156,6 +162,26 @@ local function replace_tags()
dt.print(_('Tags replaced'))
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
-- create modul Tagging addons
taglist_label.reset_callback = mcopy_tags
@@ -169,52 +195,55 @@ local taglabel = dt.new_widget("label") {
local box1 = dt.new_widget("box"){
orientation = "horizontal",
dt.new_widget("button") {
label = _('multi copy tags'),
clicked_callback = mcopy_tags},
label = _('multi copy tags'),
tooltip = _('copy tags from selected image(s)'),
clicked_callback = mcopy_tags},
dt.new_widget("button") {
label = _('paste tags'),
clicked_callback = attach_tags}
tooltip = _('paste tags to selected image(s)'),
label = _('paste tags'),
clicked_callback = attach_tags}
}
local box2 = dt.new_widget("box"){
orientation = "horizontal",
dt.new_widget("button") {
label = _('replace tags'),
clicked_callback = replace_tags},
label = _('replace tags'),
tooltip = _('replace tags from selected image(s)'),
clicked_callback = replace_tags},
dt.new_widget("button") {
label = _('remove all tags'),
clicked_callback = detach_tags}
label = _('remove all tags'),
tooltip = _('remove tags from selected image(s)'),
clicked_callback = detach_tags}
}
local sep = dt.new_widget("separator"){}
-- pack elements into widget table for a nicer layout
local widget_table = {}
widget_table[1] = box1
widget_table[#widget_table+1] = box2
cadt.widget_table[1] = box1
cadt.widget_table[#cadt.widget_table+1] = box2
widget_table[#widget_table+1] = sep
widget_table[#widget_table+1] = taglabel
widget_table[#widget_table+1] = taglist_label
cadt.widget_table[#cadt.widget_table+1] = sep
cadt.widget_table[#cadt.widget_table+1] = taglabel
cadt.widget_table[#cadt.widget_table+1] = taglist_label
-- create modul
dt.register_lib("tagging_addon","Tagging addon",true,true,{
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",500}
},
dt.new_widget("box") {
-- orientation = "vertical",
reset_callback = function()
taglist_label.label = ""
image_tags = {}
end,
table.unpack(widget_table),
},
nil,
nil
)
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not cadt.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
)
cadt.event_registered = true
end
end
-- shortcut for copy

View File

@@ -37,6 +37,7 @@ local du = require "lib/dtutils"
-- Tested with darktable 2.0.1
du.check_min_api_version("2.0.0", "cr2hdr")
local CURR_API_STRING = darktable.configuration.api_version_string
local queue = {}
local processed_files = {}
@@ -107,8 +108,11 @@ local function convert_action_images(shortcut)
convert_images()
end
darktable.register_event("shortcut", convert_action_images, "Run cr2hdr (Magic Lantern DualISO converter) on selected images")
darktable.register_event("post-import-image", file_imported)
darktable.register_event("post-import-film", film_imported)
darktable.register_event("shortcut",
convert_action_images, "Run cr2hdr (Magic Lantern DualISO converter) on selected images")
darktable.register_event("post-import-image",
file_imported)
darktable.register_event("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)

File diff suppressed because it is too large Load Diff

175
contrib/exportLUT.lua Normal file
View File

@@ -0,0 +1,175 @@
--[[
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")
local gettext = dt.gettext
gettext.bindtextdomain("exportLUT",dt.configuration.config_dir.."/lua/locale/")
local CURR_API_STRING = dt.configuration.api_version_string
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 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(
"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

476
contrib/ext_editor.lua Normal file
View File

@@ -0,0 +1,476 @@
--[[
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"
local CURR_API_STRING = dt.configuration.api_version_string
-- check API version
du.check_min_api_version("5.0.2", MODULE_NAME) -- darktable 3.x
-- 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", 100}}
if dr then
views = {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100},
[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
-- 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(
"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("shortcut",
program_shortcut, _("edit with program ")..string.format("%02d", i))
end
-- end of script --------------------------------------------------------------
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua;

View File

@@ -0,0 +1,516 @@
--[[
Face recognition for darktable
Copyright (c) 2017 Sebastian Witt
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/>.
]]
--[[
face_recognition
Add a new storage option to send images to face_recognition.
Images are exported to darktable tmp dir first.
A directory with known faces must exist, the image name are the
tag names which will be used.
Multiple images for one face can exist, add a number to it, the
number will be removed from the tag, for example:
People|IknowYou1.jpg
People|IknowYou2.jpg
People|Another.jpg
People|Youtoo.jpg
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
* https://github.com/ageitgey/face_recognition
* https://github.com/darktable-org/lua-scripts/tree/master/lib
USAGE
* require this file from your main luarc config file.
This plugin will add a new storage option and calls face_recognition after export.
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local dtsys = require "lib/dtutils.system"
local gettext = dt.gettext
-- constants
local MODULE = "face_recognition"
local PS = dt.configuration.running_os == "windows" and '\\' or '/'
local OUTPUT = dt.configuration.tmp_dir .. PS .. "facerecognition.txt"
local CURR_API_STRING = dt.configuration.api_version_string
-- namespace
local fc = {}
fc.module_installed = false
fc.event_registered = false
-- ensure we meet the minimum api
du.check_min_api_version("5.0.0", "face_recognition")
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("face_recognition", dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("face_recognition", msgid)
end
local function build_image_table(images)
local image_table = {}
local file_extension = ""
local tmp_dir = dt.configuration.tmp_dir .. PS
local ff = fc.export_format.value
local cnt = 0
-- check for plugin-data and direct_edit and build image table accordingly
if string.match(ff, "JPEG") then
file_extension = ".jpg"
elseif string.match(ff, "PNG") then
file_extension = ".png"
elseif string.match(ff, "TIFF") then
file_extension = ".tif"
end
for _,img in ipairs(images) do
if img ~= nil then
image_table[tmp_dir .. df.get_basename(img.filename) .. file_extension] = img
cnt = cnt + 1
end
end
return image_table, cnt
end
local function stop_job(job)
job.valid = false
end
local function do_export(img_tbl, images)
local exporter = nil
local upsize = false
local ff = fc.export_format.value
local height = dt.preferences.read(MODULE, "max_height", "integer")
local width = dt.preferences.read(MODULE, "max_width", "integer")
-- get the export format parameters
if string.match(ff, "JPEG") then
exporter = dt.new_format("jpeg")
exporter.quality = 80
elseif string.match(ff, "PNG") then
exporter = dt.new_format("png")
exporter.bpp = 8
elseif string.match(ff, "TIFF") then
exporter = dt.new_format("tiff")
exporter.bpp = 8
end
exporter.max_height = height
exporter.max_width = width
-- export the images
local job = dt.gui.create_job(_("export images"), true, stop_job)
local exp_cnt = 0
local percent_step = 1.0 / images
job.percent = 0.0
for export,img in pairs(img_tbl) do
exp_cnt = exp_cnt + 1
dt.print(string.format(_("Exporting image %i of %i images"), exp_cnt, images))
exporter:write_image(img, export, upsize)
job.percent = job.percent + percent_step
end
job.valid = false
-- return success, or not
return true
end
local function save_preferences()
dt.preferences.write(MODULE, "unknown_tag", "string", fc.unknown_tag.text)
dt.preferences.write(MODULE, "no_persons_found_tag", "string", fc.no_persons_found_tag.text)
dt.preferences.write(MODULE, "ignore_tags", "string", fc.ignore_tags.text)
dt.preferences.write(MODULE, "category_tags", "string", fc.category_tags.text)
dt.preferences.write(MODULE, "known_image_path", "directory", fc.known_image_path.value)
local val = fc.tolerance.value
val = string.gsub(tostring(val), ",", ".")
dt.preferences.write(MODULE, "tolerance", "float", tonumber(val))
dt.preferences.write(MODULE, "num_cores", "integer", fc.num_cores.value)
dt.preferences.write(MODULE, "export_format", "integer", fc.export_format.selected)
dt.preferences.write(MODULE, "max_width", "integer", tonumber(fc.width.text))
dt.preferences.write(MODULE, "max_height", "integer", tonumber(fc.height.text))
end
local function reset_preferences()
fc.unknown_tag.text = "unknown_person"
fc.no_persons_found_tag.text = "no_persons_found"
fc.ignore_tags.text = ""
fc.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
-- Check if image has ignored tag attached
local function ignoreByTag (image, ignoreTags)
local tags = image:get_tags ()
local ignoreImage = false
-- For each image tag
for _,t in ipairs (tags) do
-- Check if it contains a ignore tag
for _,it in ipairs (ignoreTags) do
if string.find (t.name, it, 1, true) then
-- The image has ignored tag attached
ignoreImage = true
dt.print_log ("Face recognition: Ignored tag: " .. it .. " found in " .. image.id .. ":" .. t.name)
end
end
end
return ignoreImage
end
local function cleanup(img_list)
for _, img in ipairs(img_list) do
os.remove(img)
end
os.remove(OUTPUT)
end
local function face_recognition ()
local bin_path = df.check_if_bin_exists("face_recognition")
if not bin_path then
dt.print(_("Face recognition not found"))
return
end
save_preferences()
-- Get preferences
local knownPath = dt.preferences.read(MODULE, "known_image_path", "directory")
local nrCores = dt.preferences.read(MODULE, "num_cores", "integer")
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 nonpersonsfoundTag = dt.preferences.read(MODULE, "no_persons_found_tag", "string")
-- face_recognition uses -1 for all cores, we use 0 in preferences
if nrCores < 1 then
nrCores = -1
end
-- Split ignore tags (if any)
ignoreTags = {}
for tag in string.gmatch(ignoreTagString, '([^,]+)') do
table.insert (ignoreTags, tag)
dt.print_log ("Face recognition: Ignore tag: " .. tag)
end
-- list of exported images
local image_table, cnt = build_image_table(dt.gui.action_images)
if cnt > 0 then
local success = do_export(image_table, cnt)
if success then
-- do the face recognition
local img_list = {}
for v,_ in pairs(image_table) do
table.insert (img_list, v)
end
-- Get path of exported images
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: 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 command = bin_path .. " --cpus " .. nrCores .. " --tolerance " .. tolerance .. " " .. knownPath .. " " .. path .. " > " .. OUTPUT
os.setlocale()
dt.print_log("Face recognition: Running command: " .. command)
dt.print(_("Starting face recognition..."))
dtsys.external_command(command)
-- Open output file
local f = io.open(OUTPUT, "rb")
if not f then
dt.print(_("Face recognition failed"))
else
dt.print(_("Face recognition finished"))
f:close ()
end
-- Read output
dt.print(_("processing results..."))
local result = {}
local tags_list = {}
local tag_object = {}
for line in io.lines(OUTPUT) do
if not string.match(line, "^WARNING:") and line ~= "" and line ~= nil then
local file, tag = string.match (line, "(.*),(.*)$")
tag = string.gsub (tag, "%d*$", "")
dt.print_log ("File:"..file .." Tag:".. 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
-- Attach tags
local result_index = 0
for file,tags in pairs(result) do
result_index = result_index +1
-- Find image in table
img = image_table[file]
if img == nil then
dt.print_log("Face recognition: Ignoring face recognition entry: " .. file)
else
for t,_ in pairs (tags) do
-- Check if image is ignored
if ignoreByTag (img, ignoreTags) then
dt.print_log("Face recognition: Ignoring image with ID " .. img.id)
else
-- Check of unrecognized unknown_person
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)
-- Create tag if it does not exist
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)
end
end
end
end
end
cleanup(img_list)
dt.print_log("img_list cleaned-up")
dt.print_log("face recognition complete")
dt.print(_("face recognition complete"))
else
dt.print(_("image export failed"))
return
end
else
dt.print(_("no images selected"))
return
end
end
local function install_module()
if not fc.module_installed then
dt.register_lib(
"face_recognition", -- 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
-- build the interface
fc.unknown_tag = dt.new_widget("entry"){
text = dt.preferences.read(MODULE, "unknown_tag", "string"),
tooltip = _("tag to be used for unknown person"),
editable = true,
}
fc.no_persons_found_tag = dt.new_widget("entry"){
text = dt.preferences.read(MODULE, "no_persons_found_tag", "string"),
tooltip = _("tag to be used when no persons are found"),
editable = true,
}
fc.ignore_tags = dt.new_widget("entry"){
text = dt.preferences.read(MODULE, "ignore_tags", "string"),
tooltip = _("tags of images to ignore"),
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"){
label = _("tolerance"),
tooltip = ("detection tolerance - 0.6 default - lower if too many faces detected"),
soft_min = 0.0,
hard_min = 0.0,
soft_max = 1.0,
soft_min = 1.0,
step = 0.1,
digits = 1,
value = 0.0,
}
fc.num_cores = dt.new_widget("slider"){
label = _("processor cores"),
tooltip = _("number of processor cores to use, 0 for all"),
soft_min = 0,
soft_max = 16,
hard_min = 0,
hard_max = 64,
step = 1,
digits = 0,
value = dt.preferences.read(MODULE, "num_cores", "integer"),
}
fc.known_image_path = dt.new_widget("file_chooser_button"){
title = _("known image directory"),
tooltip = _("face data directory"),
value = dt.preferences.read(MODULE, "known_image_path", "directory"),
is_directory = true,
changed_callback = function(this)
dt.preferences.write(MODULE, "known_image_path", "directory", this.value)
end
}
fc.export_format = dt.new_widget("combobox"){
label = _("export image format"),
tooltip = _("format for exported images"),
selected = dt.preferences.read(MODULE, "export_format", "integer"),
changed_callback = function(this)
dt.preferences.write(MODULE, "export_format", "integer", this.selected)
end,
"JPEG", "PNG", "TIFF",
}
fc.width = dt.new_widget("entry"){
text = tostring(dt.preferences.read(MODULE, "max_width", "integer")),
tooltip = _("maximum exported image width"),
editable = true,
}
fc.height = dt.new_widget("entry"){
text = tostring(dt.preferences.read(MODULE, "max_height", "integer")),
tooltip = _("maximum exported image height"),
editable = true,
}
fc.execute = dt.new_widget("button"){
label = "detect faces",
clicked_callback = function(this)
face_recognition()
end
}
local widgets = {
dt.new_widget("label"){ label = _("unknown person tag")},
fc.unknown_tag,
dt.new_widget("label"){ label = _("no persons found tag")},
fc.no_persons_found_tag,
dt.new_widget("label"){ label = _("tags of images to ignore")},
fc.ignore_tags,
dt.new_widget("label"){ label = _("tag category")},
fc.category_tags,
dt.new_widget("label"){ label = _("face data directory")},
fc.known_image_path,
}
if dt.configuration.running_os == "windows" or dt.configuration.running_os == "macos" then
table.insert(widgets, df.executable_path_widget({"face_recognition"}))
end
table.insert(widgets, dt.new_widget("section_label"){ label = _("processing options")})
table.insert(widgets, fc.tolerance)
table.insert(widgets, fc.num_cores)
table.insert(widgets, fc.export_format)
table.insert(widgets, dt.new_widget("box"){
orientation = "horizontal",
dt.new_widget("label"){ label = _("width ")},
fc.width,
})
table.insert(widgets, dt.new_widget("box"){
orientation = "horizontal",
dt.new_widget("label"){ label = _("height ")},
fc.height,
})
table.insert(widgets, fc.execute)
fc.widget = dt.new_widget("box"){
orientation = vertical,
reset_callback = function(this)
reset_preferences()
end,
table.unpack(widgets),
}
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not fc.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
)
fc.event_registered = true
end
end
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
--
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

View File

@@ -0,0 +1,111 @@
--[[ fujifilm_dynamic_range-0.1
Compensate for Fujifilm raw files made using "dynamic range".
Copyright (C) 2021 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("4.0.0", "fujifilm_dynamic_range")
local CURR_API_STRING = dt.configuration.api_version_string
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
dt.register_event("post-import-image",
detect_dynamic_range)
dt.print_log("[fujifilm_dynamic_range] loaded")

View File

@@ -26,9 +26,11 @@ Dependencies:
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local gettext = dt.gettext
du.check_min_api_version("4.0.0", "fujifilm_ratings")
local CURR_API_STRING = dt.configuration.api_version_string
gettext.bindtextdomain("fujifilm_ratings", dt.configuration.config_dir.."/lua/locale/")
@@ -41,7 +43,7 @@ local function detect_rating(event, image)
dt.print_error(_("exiftool not found"))
return
end
local RAF_filename = tostring(image)
local RAF_filename = df.sanitize_filename(tostring(image))
local JPEG_filename = string.gsub(RAF_filename, "%.RAF$", ".JPG")
local command = "exiftool -Rating " .. JPEG_filename
dt.print_error(command)
@@ -66,6 +68,7 @@ local function detect_rating(event, image)
end
end
dt.register_event("post-import-image", detect_rating)
dt.register_event("post-import-image",
detect_rating)
print(_("fujifilm_ratings loaded."))

View File

@@ -35,7 +35,6 @@ USAGE
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("3.0.0", "geoJSON_export")

View File

@@ -28,10 +28,11 @@ require "geoToolbox"
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("3.0.0", "geoToolbox")
local CURR_API_STRING = dt.configuration.api_version_string
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("geoToolbox",dt.configuration.config_dir.."/lua/locale/")
@@ -40,6 +41,12 @@ local function _(msgid)
return gettext.dgettext("geoToolbox", msgid)
end
local gT = {}
gT.module_installed = false
gT.event_registered = false
-- <GUI>
local labelDistance = dt.new_widget("label")
labelDistance.label = _("Distance:")
@@ -572,6 +579,22 @@ local function altitude_profile()
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 separator = dt.new_widget("separator"){}
local separator2 = dt.new_widget("separator"){}
@@ -579,13 +602,7 @@ local separator3 = dt.new_widget("separator"){}
local separator4 = dt.new_widget("separator"){}
local separator5 = dt.new_widget("separator"){}
dt.register_lib(
"geoToolbox", -- Module name
"geo toolbox", -- name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
dt.new_widget("box")
gT.widget = dt.new_widget("box")
{
orientation = "vertical",
dt.new_widget("button")
@@ -664,10 +681,24 @@ dt.register_lib(
clicked_callback = altitude_profile
},
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(
"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
dt.preferences.register("geoToolbox",
@@ -678,11 +709,15 @@ dt.preferences.register("geoToolbox",
'' )
-- Register
dt.register_event("shortcut", print_calc_distance, _("Calculate the distance from latitude and longitude in km"))
dt.register_event("mouse-over-image-changed", toolbox_calc_distance)
dt.register_event("shortcut",
print_calc_distance, _("Calculate the distance from latitude and longitude in km"))
dt.register_event("mouse-over-image-changed",
toolbox_calc_distance)
dt.register_event("shortcut", select_with_gps, _("Select all images with GPS information"))
dt.register_event("shortcut", select_without_gps, _("Select all images without GPS information"))
dt.register_event("shortcut",
select_with_gps, _("Select all images with GPS information"))
dt.register_event("shortcut",
select_without_gps, _("Select all images without GPS information"))
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua;

View File

@@ -41,7 +41,9 @@
* require this script from your main lua file
* select an image or images for editing with GIMP
* in the export dialog select "Edit with GIMP" and select the format and bit depth for the
exported image
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"
* Edit the image with GIMP then save the changes with File->Overwrite....
* Exit GIMP
@@ -103,6 +105,8 @@ end
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")
if not gimp_executable then
@@ -111,7 +115,11 @@ local function gimp_edit(storage, image_table, extra_data) --finalize
end
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
-- list of exported images
@@ -130,54 +138,67 @@ local function gimp_edit(storage, image_table, extra_data) --finalize
local gimpStartCommand
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)
dtsys.external_command(gimpStartCommand)
-- for each of the image, exported image pairs
-- move the exported image into the directory with the original
-- then import the image into the database which will group it with the original
-- and then copy over any tags other than darktable tags
if not run_detached then
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
myimage_name = df.filename_increment(myimage_name)
-- limit to 99 more exports of the original export
if string.match(df.get_basename(myimage_name), "_(d-)$") == "99" then
break
local myimage_name = image.path .. "/" .. df.get_filename(exported_image)
while df.check_if_file_exists(myimage_name) do
myimage_name = df.filename_increment(myimage_name)
-- limit to 99 more exports of the original export
if string.match(df.get_basename(myimage_name), "_(d-)$") == "99" then
break
end
end
end
dt.print_log("moving " .. exported_image .. " to " .. myimage_name)
local result = df.file_move(exported_image, myimage_name)
dt.print_log("moving " .. exported_image .. " to " .. myimage_name)
local result = df.file_move(exported_image, myimage_name)
if result then
dt.print_log("importing file")
local myimage = dt.database.import(myimage_name)
if result then
dt.print_log("importing file")
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
if not (string.sub(tag.name,1,9) == "darktable") then
dt.print_log("attaching tag")
dt.tags.attach(tag,myimage)
for _,tag in pairs(dt.tags.get_tags(image)) do
if not (string.sub(tag.name,1,9) == "darktable") then
dt.print_log("attaching tag")
dt.tags.attach(tag,myimage)
end
end
end
end
end
end
-- Register
local executables = {"gimp"}
if dt.configuration.running_os ~= "linux" then
gimp_widget = df.executable_path_widget(executables)
end
gimp_widget = dt.new_widget("check_button"){
label = _("run detached"),
tooltip = _("don't import resulting image back into darktable"),
value = dt.preferences.read("gimp", "run_detached", "bool"),
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)

View File

@@ -25,9 +25,11 @@ For each source folder, a separate <trk> is generated in the gpx file.
local dt = require "darktable"
local df = require "lib/dtutils.file"
local dl = require "lib/dtutils"
local gettext = dt.gettext
dl.check_min_api_version("3.0.0", "gpx-export")
local CURR_API_STRING = dt.configuration.api_version_string
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("gpx_export",dt.configuration.config_dir.."/lua/locale/")
@@ -36,6 +38,10 @@ local function _(msgid)
return gettext.dgettext("gpx_export", msgid)
end
local gpx = {}
gpx.module_installed = false
gpx.event_registered = false
local path_entry = dt.new_widget("entry")
{
text = dt.preferences.read("gpx_exporter", "gpxExportPath", "string"),
@@ -127,31 +133,55 @@ local function create_gpx_file()
end
end
dt.register_lib(
"gpx_exporter",
"gpx export",
true, -- expandable
true, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
local function install_module()
if not gpx.module_installed then
dt.register_lib(
"gpx_exporter",
"gpx export",
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
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")
{
orientation = "vertical",
dt.new_widget("button")
orientation = "horizontal",
dt.new_widget("label")
{
label = _("export"),
tooltip = _("export gpx file"),
clicked_callback = create_gpx_file
},
dt.new_widget("box")
{
orientation = "horizontal",
dt.new_widget("label")
{
label = _("file:"),
},
path_entry
label = _("file:"),
},
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(
"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

View File

@@ -40,7 +40,6 @@ local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local log = require "lib/dtutils.log"
local dtsys = require "lib/dtutils.system"
require "official/yield"
local gettext = dt.gettext
local namespace = 'module_hugin'
@@ -227,4 +226,4 @@ dt.register_storage(namespace, _("hugin panorama"), show_status, create_panorama
--
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

566
contrib/image_time.lua Normal file
View File

@@ -0,0 +1,566 @@
--[[
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("3.0.0", "image_time")
local CURR_API_STRING = dt.configuration.api_version_string
-- 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
-- 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(
"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

View File

@@ -41,7 +41,6 @@ format (all fields can be the empty string):
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
require "official/yield"
local gettext = dt.gettext

475
contrib/photils.lua Normal file
View File

@@ -0,0 +1,475 @@
--[[ 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("5.0.0", MODULE_NAME)
local CURR_API_STRING = dt.configuration.api_version_string
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
-- 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("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(
"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

View File

@@ -47,10 +47,17 @@ local dt = require "darktable"
local du = require "lib/dtutils"
local debug = require "darktable.debug"
local qt = {}
qt.module_installed = false
qt.event_registered = false
qt.widget_table = {}
local gettext = dt.gettext
du.check_min_api_version("3.0.0", "quicktag")
local CURR_API_STRING = dt.configuration.api_version_string
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("quicktag",dt.configuration.config_dir.."/lua/locale/")
@@ -177,12 +184,33 @@ local function update_quicktag_list()
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
update_quicktag_list()
local new_quicktag = dt.new_widget("entry"){
text = "",
placeholder = _("new tag"),
is_password = true,
is_password = false,
editable = true,
tooltip = _("enter your tag here")
}
@@ -213,33 +241,32 @@ local new_qt_widget = dt.new_widget ("box") {
-- back UI elements in a table
-- thanks to wpferguson for the hint
local widget_table = {}
for i=1,qnr do
widget_table[#widget_table + 1] = button[i]
qt.widget_table[#qt.widget_table + 1] = button[i]
end
widget_table[#widget_table + 1] = dt.new_widget("separator"){}
widget_table[#widget_table + 1] = old_quicktag
widget_table[#widget_table + 1] = new_qt_widget
qt.widget_table[#qt.widget_table + 1] = dt.new_widget("separator"){}
qt.widget_table[#qt.widget_table + 1] = old_quicktag
qt.widget_table[#qt.widget_table + 1] = new_qt_widget
--create module
dt.register_lib(
"quicktag", -- Module name
"quicktag", -- name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 490}},
dt.new_widget("box"){
orientation = "vertical",
table.unpack(widget_table),
},
nil,-- view_enter
nil -- view_leave
)
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not qt.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
)
qt.event_registered = true
end
end
-- create shortcuts
for i=1,qnr do

View File

@@ -43,6 +43,7 @@ local du = require "lib/dtutils"
-- added version check
du.check_min_api_version("3.0.0", "rate_group")
local CURR_API_STRING = dt.configuration.api_version_string
local function apply_rating(rating)
local images = dt.gui.action_images
@@ -59,30 +60,37 @@ local function apply_rating(rating)
end
end
dt.register_event("shortcut",function(event, shortcut)
dt.register_event("shortcut",
function(event, shortcut)
apply_rating(-1)
end, "Reject group")
dt.register_event("shortcut",function(event, shortcut)
dt.register_event("shortcut",
function(event, shortcut)
apply_rating(0)
end, "Rate group 0")
dt.register_event("shortcut",function(event, shortcut)
dt.register_event("shortcut",
function(event, shortcut)
apply_rating(1)
end, "Rate group 1")
dt.register_event("shortcut",function(event, shortcut)
dt.register_event("shortcut",
function(event, shortcut)
apply_rating(2)
end, "Rate group 2")
dt.register_event("shortcut",function(event, shortcut)
dt.register_event("shortcut",
function(event, shortcut)
apply_rating(3)
end, "Rate group 3")
dt.register_event("shortcut",function(event, shortcut)
dt.register_event("shortcut",
function(event, shortcut)
apply_rating(4)
end, "Rate group 4")
dt.register_event("shortcut",function(event, shortcut)
dt.register_event("shortcut",
function(event, shortcut)
apply_rating(5)
end, "Rate group 5")

View File

@@ -34,6 +34,11 @@ local debug = require "darktable.debug"
-- check API version
du.check_min_api_version("3.0.0", "rename-tags")
local CURR_API_STRING = darktable.configuration.api_version_string
local rt = {}
rt.module_installed = false
rt.event_registered = false
-- GUI entries
local old_tag = darktable.new_widget("entry") { tooltip = "Enter old tag" }
@@ -99,6 +104,13 @@ local function rename_tags()
rename_reset()
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
-- GUI
local old_widget = darktable.new_widget ("box") {
orientation = "horizontal",
@@ -112,7 +124,7 @@ local new_widget = darktable.new_widget ("box") {
new_tag
}
local rename_widget = darktable.new_widget ("box") {
rt.rename_widget = darktable.new_widget ("box") {
orientation = "vertical",
reset_callback = rename_reset,
old_widget,
@@ -120,6 +132,20 @@ local rename_widget = darktable.new_widget ("box") {
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(
"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
darktable.register_lib ("rename_tags", "rename tag", true, true, {[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 20},}, rename_widget, nil, nil)

View File

@@ -26,6 +26,7 @@ du.check_min_api_version("3.0.0", "select_untagged")
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("select_untagged",dt.configuration.config_dir.."/lua/locale/")
local CURR_API_STRING = dt.configuration.api_version_string
local function _(msgid)
return gettext.dgettext("select_untagged", msgid)
@@ -35,23 +36,23 @@ local function stop_job(job)
job.valid = false
end
local function select_untagged_images()
local function select_untagged_images(event, images)
job = dt.gui.create_job(_("select untagged images"), true, stop_job)
local selection = {}
for key,image in ipairs(dt.collection) do
for key,image in ipairs(images) do
if(job.valid) then
job.percent = (key-1)/#dt.collection
job.percent = (key - 1)/#images
local tags = dt.tags.get_tags(image)
local hasTags = false
for _,tag in ipairs(tags) do
if not string.match(tag.name,"darktable|") then
if not string.match(tag.name, "darktable|") then
hasTags = true
end
end
if hasTags == false then
table.insert(selection,image)
table.insert(selection, image)
end
else
break
@@ -59,7 +60,11 @@ local function select_untagged_images()
end
job.valid = false
dt.gui.selection(selection)
-- return table of images to set the selection to
return selection
end
dt.gui.libs.select.register_selection(_("select untagged"),select_untagged_images,_("select all images containing no tags or only tags added by darktable"))
dt.gui.libs.select.register_selection(
_("select untagged"),
select_untagged_images,
_("select all images containing no tags or only tags added by darktable"))

View File

@@ -27,11 +27,13 @@ USAGE
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.2", "slideshowMusic")
local CURR_API_STRING = dt.configuration.api_version_string
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("slideshowMusic",dt.configuration.config_dir.."/lua/locale/")
@@ -77,4 +79,5 @@ dt.preferences.register("slideshowMusic",
_("Plays music with rhythmbox if a slideshow starts"),
true)
-- Register
dt.register_event("view-changed",playSlideshowMusic)
dt.register_event("view-changed",
playSlideshowMusic)

379
contrib/transfer_hierarchy.lua Executable file
View File

@@ -0,0 +1,379 @@
--[[
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("5.0.0", LIB_ID)
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 .. "]+)"
local CURR_API_STRING = darktable.configuration.api_version_string
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
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(
"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

View File

@@ -96,7 +96,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 = {
["AVI"] = {

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

@@ -0,0 +1,98 @@
--[[
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
-- - - - - - - - - - - - - - - - - - - - - - - -
-- 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()
-- 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)

View File

@@ -0,0 +1,198 @@
--[[
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 "/"
-- - - - - - - - - - - - - - - - - - - - - - - -
-- 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",
}
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))
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
-- cycle through sorts
dt.print(_("lighttable sorting demonstration"))
dt.gui.libs.lighttable_mode.layout("DT_LIGHTTABLE_LAYOUT_FILEMANAGER")
dt.gui.libs.lighttable_mode.zoom_level(5)
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)

View File

@@ -18,9 +18,9 @@
--[[
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:
require "moduleExample"
require "examples/moduleExample"
* it creates a new example lighttable module
@@ -34,36 +34,94 @@ local dt = require "darktable"
local du = require "lib/dtutils"
du.check_min_api_version("3.0.0", "moduleExample")
local CURR_API_STRING = dt.configuration.api_version_string
-- add a new lib
local check_button = dt.new_widget("check_button"){label = "MyCheck_button", value = true}
local combobox = dt.new_widget("combobox"){label = "MyCombobox", value = 2, "8", "16", "32"}
-- translation
--https://www.darktable.org/lua-api/ar01s02s54.html.php
-- 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
-- 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")
{
text = "test",
placeholder = "placeholder",
placeholder = _("placeholder"),
is_password = false,
editable = true,
tooltip = "Tooltip Text",
tooltip = _("Tooltip Text"),
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")
{
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
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")
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"){}
-- https://www.darktable.org/lua-api/types_lua_slider.html
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_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
@@ -71,68 +129,34 @@ local slider = dt.new_widget("slider")
value = 52 -- The current value of the slider
}
if (dt.configuration.api_version_major >= 6) then
local section_label = dt.new_widget("section_label")
{
label = "MySectionLabel"
}
-- pack the widgets in a table for loading in the module
dt.register_lib(
"exampleModule", -- Module name
"exampleModule", -- name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
dt.new_widget("box") -- widget
{
orientation = "vertical",
dt.new_widget("button")
{
label = "MyButton",
clicked_callback = function (_)
dt.print("Button clicked")
end
},
check_button,
combobox,
entry,
file_chooser_button,
label,
separator,
slider,
section_label
},
nil,-- view_enter
nil -- view_leave
)
table.insert(mE.widgets, check_button)
table.insert(mE.widgets, combobox)
table.insert(mE.widgets, entry)
table.insert(mE.widgets, file_chooser_button)
table.insert(mE.widgets, label)
table.insert(mE.widgets, separator)
table.insert(mE.widgets, slider)
-- ... and tell dt about it all
if dt.gui.current_view().id == "lighttable" then -- make sure we are in lighttable view
install_module() -- register the lib
else
dt.register_lib(
"exampleModule", -- Module name
"exampleModule", -- name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
dt.new_widget("box") -- widget
{
orientation = "vertical",
dt.new_widget("button")
{
label = "MyButton",
clicked_callback = function (_)
dt.print("Button clicked")
end
},
check_button,
combobox,
entry,
file_chooser_button,
label,
separator,
slider
},
nil,-- view_enter
nil -- view_leave
)
if not mE.event_registered then -- if we are not in lighttable view then register an event to signal when we might be
-- https://www.darktable.org/lua-api/index.html#darktable_register_event
dt.register_event(
"view-changed", -- we want to be informed when the view changes
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then -- if the view changes from darkroom to lighttable
install_module() -- register the lib
end
end
)
mE.event_registered = true -- keep track of whether we have an event handler installed
end
end
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

View File

@@ -52,7 +52,7 @@
]]
local dt = require "darktable"
--[[
require "lib/..." provides access to functions that have been pulled from various
scripts and consolidated into libraries. Using the libraries eliminates having to
@@ -62,6 +62,7 @@ local dt = require "darktable"
local du = require "lib/dtutils" -- utilities
local df = require "lib/dtutils.file" -- file utilities
local dtsys = require "lib/dtutils.system" -- system utilities
local CURR_API_STRING = dt.configuration.api_version_string
--[[
darktable is an international program, and it's user interface has been translated into
@@ -204,24 +205,26 @@ end
to see if the executable is there.
]]
local executable = "ufraw-batch"
local ufraw_batch_path_widget = dt.new_widget("file_chooser_button"){
title = _("Select ufraw-batch[.exe] executable"),
value = df.get_executable_path_preference(executable),
is_directory = false,
changed_callback = function(self)
if df.check_if_bin_exists(self.value) then
df.set_executable_path_preference(executable, self.value)
if dt.configuration.running_os ~= "linux" then
local executable = "ufraw-batch"
local ufraw_batch_path_widget = dt.new_widget("file_chooser_button"){
title = _("Select ufraw-batch[.exe] executable"),
value = df.get_executable_path_preference(executable),
is_directory = false,
changed_callback = function(self)
if df.check_if_bin_exists(self.value) then
df.set_executable_path_preference(executable, self.value)
end
end
end
}
dt.preferences.register("executable_paths", "ufraw-batch", -- name
"file", -- type
_('multi_os: ufraw-batch location'), -- label
_('Installed location of ufraw-batch. Requires restart to take effect.'), -- tooltip
"ufraw-batch", -- default
ufraw_batch_path_widget
)
}
dt.preferences.register("executable_paths", "ufraw-batch", -- name
"file", -- type
_('multi_os: ufraw-batch location'), -- label
_('Installed location of ufraw-batch. Requires restart to take effect.'), -- tooltip
"ufraw-batch", -- default
ufraw_batch_path_widget
)
end
--[[
Add a button to the selected images module in lighttable
@@ -232,6 +235,7 @@ dt.gui.libs.image.register_action(
function(event, images) extract_embedded_jpeg(images) end,
"extract embedded jpeg"
)
--[[
Add a shortcut

136
examples/panels_demo.lua Normal file
View File

@@ -0,0 +1,136 @@
--[[
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("5.0.2", "panels_demo") -- darktable 3.0
-- - - - - - - - - - - - - - - - - - - - - - - -
-- 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

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

@@ -283,4 +283,51 @@ function dtutils.check_os(operating_systems)
return false
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
return dtutils

View File

@@ -35,6 +35,316 @@ local function _(msgid)
return gettext.dgettext("dtutils.file", msgid)
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("which " .. 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"] = {
Name = [[check_if_bin_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]],
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
registered and uses that if found. The presence of the file is verified, then
quoted and returned. If no preference is specified and the operating system is
linux then the which command is used to check for a binary in the path. If found
that path is returned. If no binary is found, false is returned.]],
Return_Value = [[result - string - the path of the binary, false if not found]],
Limitations = [[]],
registered and uses that if found, after it's verified to be an executable and
exist. If no preference exissts, the user's path is checked for the executable.
If the executable is not found in the users path, then a search of the operating
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]],
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 = [[]],
See_Also = [[]],
See_Also = [[executable_manager]],
Reference = [[]],
License = [[]],
Copyright = [[]],
@@ -59,28 +380,15 @@ dtutils_file.libdoc.functions["check_if_bin_exists"] = {
function dtutils_file.check_if_bin_exists(bin)
local result = false
local path = nil
if string.match(bin, "/") or string.match(bin, "\\") then
path = bin
if dt.configuration.running_os == "windows" and dt.preferences.read("dtutils.file", "use_old_check_if_bin_exists", "bool") then
result = _old_check_if_bin_exists(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$")) and dt.configuration.running_os ~= "windows" then
result = "wine " .. "\"" .. path .. "\""
else
result = "\"" .. 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 = output:sub(1,-2)
result = _check_path_for_bin(bin)
if not result then
result = _search_for_bin(bin)
end
end
return result
@@ -107,9 +415,14 @@ dtutils_file.libdoc.functions["split_filepath"] = {
function dtutils_file.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
return result
end
@@ -222,15 +535,21 @@ dtutils_file.libdoc.functions["check_if_file_exists"] = {
}
function dtutils_file.check_if_file_exists(filepath)
local result
local result = false
if (dt.configuration.running_os == 'windows') then
filepath = string.gsub(filepath, '[\\/]+', '\\')
result = os.execute('if exist "'..filepath..'" (cmd /c exit 0) else (cmd /c exit 1)')
if not result then
result = false
local p = io.popen("if exist " .. dtutils_file.sanitize_filename(filepath) .. " (echo 'yes') else (echo 'no')")
local ans = p:read("*all")
p:close()
if string.match(ans, "yes") then
result = true
end
-- result = os.execute('if exist "'..filepath..'" (cmd /c exit 0) else (cmd /c exit 1)')
-- if not result then
-- result = false
-- end
elseif (dt.configuration.running_os == "linux") then
result = os.execute('test -e ' .. "\"" .. filepath .. "\"")
result = os.execute('test -e ' .. dtutils_file.sanitize_filename(filepath))
if not result then
result = false
end
@@ -266,7 +585,11 @@ dtutils_file.libdoc.functions["chop_filetype"] = {
function dtutils_file.chop_filetype(path)
local length = dtutils_file.get_filetype(path):len() + 2
return string.sub(path, 1, -length)
if length > 2 then
return string.sub(path, 1, -length)
else
return path
end
end
dtutils_file.libdoc.functions["file_copy"] = {
@@ -368,12 +691,12 @@ dtutils_file.libdoc.functions["filename_increment"] = {
local result = df.filename_increment(filepath)
filepath - string - filename to increment]],
Description = [[filename_increment solves the problem of filename confllict by adding an
increment to the filename. If the supplied filename has no increment then
Description = [[filename_increment solves the problem of filename confllict by adding an
increment to the filename. If the supplied filename has no increment then
"01" is added to the basename. If the filename already has an increment, then
1 is added to it and the filename returned.]],
Return_Value = [[result - string - the incremented filename]],
Limitations = [[]],
Limitations = [[The filename will be incremented to 99]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
@@ -396,6 +719,9 @@ function dtutils_file.filename_increment(filepath)
if string.len(increment) > 2 then
-- we got the filename so set the increment to 01
increment = "01"
elseif increment == "99" then
dt.print_error("not incrementing, filename has already been incremented 99 times.")
return filepath
else
increment = string.format("%02d", tonumber(increment) + 1)
basename = string.gsub(basename, "_(%d-)$", "")
@@ -418,7 +744,7 @@ dtutils_file.libdoc.functions["create_unique_filename"] = {
filepath - string - the path and filename requested]],
Description = [[create_unique_filename takes a requested filepath and checks to see if
it exists. If if doesn't then it's returned intact. If it already exists, then a two
digit increment is added to the filename and it is tested again. The increment keeps
digit increment is added to the filename and it is tested again. The increment keeps
increasing until either a unique filename is found or there have been 100 attempts.]],
Return_Value = [[result - string - the incremented filename]],
Limitations = [[create_unique_filename will only attempt 100 increments.]],
@@ -433,8 +759,9 @@ function dtutils_file.create_unique_filename(filepath)
while dtutils_file.check_if_file_exists(filepath) do
filepath = dtutils_file.filename_increment(filepath)
-- limit to 99 more exports of the original export
if string.match(dtutils_file.get_basename(filepath), "_(d-)$") == "99" then
break
local increment = string.match(dtutils_file.get_basename(filepath), "_(%d-)$")
if increment == "99" then
break
end
end
return filepath
@@ -496,7 +823,7 @@ dtutils_file.libdoc.functions["executable_path_widget"] = {
local widget = df.executable_path_widget(executables)
executables - table - a table of strings that are 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.]],
Return_Value = [[widget - widget - a widget containing a file selector widget for
each executable.]],
@@ -511,10 +838,10 @@ dtutils_file.libdoc.functions["executable_path_widget"] = {
function dtutils_file.executable_path_widget(executables)
local box_widgets = {}
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"})
local path = dtutils_file.get_executable_path_preference(executable)
if not path then
if not path then
path = ""
end
table.insert(box_widgets, dt.new_widget("file_chooser_button"){
@@ -525,8 +852,9 @@ function dtutils_file.executable_path_widget(executables)
if dtutils_file.check_if_bin_exists(self.value) then
dtutils_file.set_executable_path_preference(executable, self.value)
end
end}
)
end
}
)
end
local box = dt.new_widget("box"){
orientation = "vertical",
@@ -543,7 +871,7 @@ dtutils_file.libdoc.functions["sanitize_filename"] = {
local sanitized_filename = df.sanitize_filename(filename)
filename - string - a filepath and filename]],
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.]],
Return_Value = [[sanitized_filename - string - quoted filename]],
Limitations = [[]],
@@ -565,7 +893,7 @@ dtutils_file.libdoc.functions["mkdir"] = {
df.mkdir(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
]],
Return_Value = [[path - string - a directory path]],
@@ -576,7 +904,8 @@ dtutils_file.libdoc.functions["mkdir"] = {
License = [[]],
Copyright = [[]],
}
function dtutils_file.mkdir(path)
function dtutils_file.mkdir(path)
if not dtutils_file.check_if_file_exists(path) then
local mkdir_cmd = dt.configuration.running_os == "windows" and "mkdir" or "mkdir -p"
return dsys.external_command(mkdir_cmd.." "..path)
@@ -607,6 +936,54 @@ function dtutils_file.rmdir(path)
return dsys.external_command(rm_cmd.." "..path)
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

View File

@@ -51,54 +51,63 @@ local dt_print_error = dt.print_error
local dt_print_log = dt.print_log
local dt_print = dt.print
-- set the default log levels
dtutils_log.debug = {
label = "DEBUG:",
label = "DEBUG: ",
enabled = false,
engine = dt_print_log,
caller_info = 3,
level = 1,
}
dtutils_log.info = {
label = "INFO:",
label = "INFO: ",
enable = false,
engine = dt_print_log,
caller_info = 1,
level = 2,
}
dtutils_log.warn = {
label = "WARN:",
label = "WARN: ",
enabled = false,
engine = dt_print_log,
caller_info = 2,
level = 3,
}
dtutils_log.error = {
label = "ERROR:",
label = "ERROR: ",
enabled = true,
engine = dt_print_error,
engine = dt_print_log,
caller_info = 3,
level = 4,
}
dtutils_log.success = {
label = "SUCCESS:",
label = "SUCCESS: ",
enabled = true,
engine = dt_print_log,
caller_info = 2,
level = 5,
}
dtutils_log.screen = {
label = "",
enabled = true,
engine = dt_print,
caller_info = 0,
level = 9,
}
dtutils_log.always = {
label = "",
enabled = true,
engine = dt_print_log,
caller_info = 3,
level = 9,
}
dtutils_log.critical = {
label = "CRITICAL:",
label = "CRITICAL: ",
enabled = true,
engine = print,
caller_info = 3,
level = 9,
}
@@ -120,7 +129,7 @@ result = log.caller(level)
Copyright = [[]],
}
function dtutils_log.caller(level)
function dtutils_log.caller(level, info)
local name = debug.getinfo(level).name
local lineno = nil
local source = nil
@@ -131,7 +140,16 @@ function dtutils_log.caller(level)
-- we just need the filename, so grab it from the string
-- Thanks, Tobias Jakobs :-)
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
return "callback:"
end
@@ -182,7 +200,7 @@ function dtutils_log.msg(level, ...)
end
local log_msg = level.label
if level.engine ~= dt_screen and call_level ~= 0 then
log_msg = log_msg .. dtutils_log.caller(call_level) .. " "
log_msg = log_msg .. dtutils_log.caller(call_level, level.caller_info) .. " "
elseif log_msg:len() > 2 then
log_msg = log_msg .. " "
end

View File

@@ -167,13 +167,66 @@ dtutils_string.libdoc.functions["urlencode"] = {
function dtutils_string.urlencode(str)
if (str) then
str = string.gsub (str, "\n", "\r\n")
str = string.gsub (str, "([^%w ])", function () return string.format ("%%%02X", string.byte()) end)
str = string.gsub (str, "([^%w ])", function (c) return string.format ("%%%02X", string.byte(c)) end)
str = string.gsub (str, " ", "+")
end
return str
end
dtutils_string.libdoc.functions["is_not_sanitized"] = {
Name = [[is_not_sanitized]],
Synopsis = [[Check if a string has been sanitized]],
Usage = [[local ds = require "lib/dtutils.string"
local result = ds.is_not_sanitized(str)
str - string - the string that needs to be made safe]],
Description = [[is_not_sanitized checks a string to see if it
has been made safe use passing as an argument in a system command.]],
Return_Value = [[result - boolean - true if the string is not sanitized otherwise false]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
local function _is_not_sanitized_posix(str)
-- A sanitized string must be quoted.
if not string.match(str, "^'.*'$") then
return true
-- A quoted string containing no quote characters within is sanitized.
elseif string.match(str, "^'[^']*'$") then
return false
end
-- Any quote characters within a sanitized string must be properly
-- escaped.
local quotesStripped = string.sub(str, 2, -2)
local escapedQuotesRemoved = string.gsub(quotesStripped, "'\\''", "")
if string.find(escapedQuotesRemoved, "'") then
return true
else
return false
end
end
local function _is_not_sanitized_windows(str)
if not string.match(str, "^\".*\"$") then
return true
else
return false
end
end
function dtutils_string.is_not_sanitized(str)
if dt.configuration.running_os == "windows" then
return _is_not_sanitized_windows(str)
else
return _is_not_sanitized_posix(str)
end
end
dtutils_string.libdoc.functions["sanitize"] = {
Name = [[sanitize]],
Synopsis = [[surround a string in quotes making it safe to pass as an argument]],
@@ -192,16 +245,55 @@ dtutils_string.libdoc.functions["sanitize"] = {
Copyright = [[]],
}
function dtutils_string.sanitize(str)
local result = ""
if dt.configuration.running_os == "windows" then
result = '"' .. str .. '"'
local function _sanitize_posix(str)
if _is_not_sanitized_posix(str) then
return "'" .. string.gsub(str, "'", "'\\''") .. "'"
else
result = "'" .. str .. "'"
return str
end
return result
end
local function _sanitize_windows(str)
if _is_not_sanitized_windows(str) then
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"
local result = ds.sanitize_lua(str)
str - string - the string that needs to be made safe]],
Description = [[sanitize_lua escapes lua 'magic' characters so that
a string may be used in lua string/patten matching.]],
Return_Value = [[result - string - a lua pattern safe string]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_string.sanitize_lua(str)
str = string.gsub(str, "%-", "%%-")
str = string.gsub(str, "%(", "%%(")
str = string.gsub(str, "%)", "%%)")
return str
end
return dtutils_string

View File

@@ -1,7 +1,6 @@
local dtutils_system = {}
local dt = require "darktable"
require "official/yield" -- necessary for dt.control.execute()
dtutils_system.libdoc = {
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

@@ -7,21 +7,29 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-09-07 22:25-0400\n"
"PO-Revision-Date: 2016-10-03 14:55+0200\n"
"Last-Translator: Pascal Obry <pascal@obry.net>\n"
"POT-Creation-Date: 2020-12-27 17:55+0100\n"
"PO-Revision-Date: 2020-12-27 17:55+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 1.8.9\n"
"Language: fr_FR\n"
"X-Generator: Poedit 2.3\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"
"X-Poedit-Basepath: .\n"
"Language-Team: \n"
"X-Poedit-Basepath: ../../../contrib\n"
"X-Poedit-SearchPath-0: clear_GPS.lua\n"
#: clear_GPS.lua:62
#: clear_GPS.lua:67
msgid "clear GPS data"
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,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,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

@@ -27,8 +27,10 @@ USAGE
local dt = require "darktable"
local du = require "lib/dtutils"
local gettext = dt.gettext
du.check_min_api_version("3.0.0", "copy_paste_metadata")
local CURR_API_STRING = dt.configuration.api_version_string
-- set this to "false" if you don't want to overwrite metadata fields
-- (title, description, creator, publisher and rights) that are already set
@@ -48,6 +50,13 @@ local publisher = ""
local rights = ""
local tags = {}
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("copy_paste_metadata",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("copy_paste_metadata", msgid)
end
local function copy(image)
if not image then
have_data = false
@@ -105,15 +114,15 @@ local function paste(images)
end
dt.gui.libs.image.register_action(
"copy metadata",
_("copy metadata"),
function(event, images) copy(images[1]) end,
"copy metadata of the first selected image"
_("copy metadata of the first selected image")
)
dt.gui.libs.image.register_action(
"paste metadata",
_("paste metadata"),
function(event, images) paste(images) end,
"paste metadata to the selected images"
_("paste metadata to the selected images")
)
dt.register_event(

View File

@@ -38,6 +38,7 @@ local df = require "lib/dtutils.file"
local dtsys = require "lib/dtutils.system"
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
local CURR_API_STRING = dt.configuration.api_version_string
local gettext = dt.gettext
@@ -46,199 +47,243 @@ du.check_min_api_version("3.0.0", "enfuse")
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("enfuse",dt.configuration.config_dir..PS .. "lua" .. PS .. "locale" .. PS)
local enf = {}
enf.event_registered = false
enf.module_installed = false
enf.lib_widgets = {}
local function _(msgid)
return gettext.dgettext("enfuse", msgid)
end
local function install_module()
if not enf.module_installed then
dt.register_lib(
"enfuse", -- plugin name
"enfuse", -- name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
dt.new_widget("box") -- widget
{
orientation = "vertical",
sensitive = enfuse_installed,
table.unpack(enf.lib_widgets)
},
nil,-- view_enter
nil -- view_leave
)
enf.module_installed = true
end
end
-- add a new lib
-- is enfuse installed?
local enfuse_installed = df.check_if_bin_exists("enfuse")
-- instance of DT tiff exporter
local tiff_exporter = dt.new_format("tiff")
tiff_exporter.bpp = 16
tiff_exporter.max_height = 0
tiff_exporter.max_width = 0
if enfuse_installed then
dt.print_log("found enfuse executable at " .. enfuse_installed)
-- check the version so that we can use the correct arguments
-- instance of DT tiff exporter
local tiff_exporter = dt.new_format("tiff")
tiff_exporter.bpp = 16
tiff_exporter.max_height = 0
tiff_exporter.max_width = 0
local version = nil
-- check the version so that we can use the correct arguments
local p = io.popen(enfuse_installed .. " --version")
local f = p:read("all")
p:close()
version = string.match(f, "enfuse (%d.%d)")
dt.print_log("enfuse version is " .. version)
local version = nil
local p = io.popen(enfuse_installed .. " --version")
local f = p:read("all")
p:close()
version = string.match(f, "enfuse (%d.%d)")
dt.print_log("enfuse version is " .. version)
-- initialize exposure_mu value and depth setting in config to sane defaults (would be 0 otherwise)
if dt.preferences.read("enfuse", "depth", "integer") == 0 then
dt.preferences.write("enfuse", "depth", "integer", 2)
dt.preferences.write("enfuse", "exposure_mu", "float", 0.5)
dt.preferences.write("enfuse", "exposure_optimum", "float", 0.5)
end
-- set up some widgets, initialized from config
local exposure_mu = nil
if version < "4.2" then
exposure_mu = dt.new_widget("slider")
{
label = "exposure mu",
tooltip = "center also known as MEAN of Gaussian weighting function (0 <= MEAN <= 1); default: 0.5",
hard_min = 0,
hard_max = 1,
value = dt.preferences.read("enfuse", "exposure_mu", "float")
}
else
exposure_mu = dt.new_widget("slider")
{
label = "exposure optimum",
tooltip = "optimum exposure value, usually the maximum of the weighting function (0 <= OPTIMUM <=1); default 0.5",
hard_min = 0,
hard_max = 1,
value = dt.preferences.read("enfuse", "exposure_optimum", "float")
}
end
local depth = dt.new_widget("combobox")
{
label = "depth",
tooltip = "the number of bits per channel of the output image",
value = dt.preferences.read("enfuse", "depth", "integer"),
changed_callback = function(w) dt.preferences.write("enfuse", "depth", "integer", w.selected) end,
"8", "16", "32"
}
local enfuse_button = dt.new_widget("button")
{
label = enfuse_installed and "run enfuse" or "enfuse not installed",
clicked_callback = function ()
-- remember exposure_mu
-- TODO: find a way to save it whenever the value changes
local mu = exposure_mu.value
if version < "4.2" then
dt.preferences.write("enfuse", "exposure-mu", "float", mu)
else
dt.preferences.write("enfuse", "exposure-optimum", "float", mu)
end
-- create a temp response file
local response_file = os.tmpname()
if dt.configuration.running_os == "windows" then
response_file = dt.configuration.tmp_dir .. response_file -- windows os.tmpname() defaults to root directory
end
local f = io.open(response_file, "w")
if not f then
dt.print(string.format(_("Error writing to `%s`"), response_file))
os.remove(response_file)
return
end
-- add all filenames to the response file
local cnt = 0
local n_skipped = 0
local target_dir
for i_, i in ipairs(dt.gui.action_images) do
-- only use ldr files as enfuse can't open raws
if i.is_ldr then
cnt = cnt + 1
f:write(i.path..PS..i.filename.."\n")
target_dir = i.path
-- alternatively raws will be exported as tiff
elseif i.is_raw then
local tmp_exported = os.tmpname()..".tif"
if dt.configuration.running_os == "windows" then
tmp_exported = dt.configuration.tmp_dir .. tmp_exported -- windows os.tmpname() defaults to root directory
end
dt.print(string.format(_("Converting raw file '%s' to tiff..."), i.filename))
tiff_exporter:write_image(i, tmp_exported, false)
dt.print_log(string.format("Raw file '%s' converted to '%s'", i.filename, tmp_exported))
cnt = cnt + 1
f:write(tmp_exported.."\n")
target_dir = i.path
-- other images will be skipped
else
dt.print(string.format(_("Skipping %s..."), i.filename))
n_skipped = n_skipped + 1
end
end
f:close()
-- bail out if there is nothing to do
if cnt == 0 then
dt.print(_("No suitable images selected, nothing to do for enfuse"))
os.remove(response_file)
return
end
if n_skipped > 0 then
dt.print(string.format(_("%d image(s) skipped"), n_skipped))
end
-- call enfuse on the response file
-- TODO: find something nicer
local ugly_decimal_point_hack = string.gsub(string.format("%.04f", mu), ",", ".")
-- TODO: make filename unique
local output_image = target_dir.. PS .. "enfuse.tif"
local exposure_option = " --exposure-optimum "
if version < "4.2" then
exposure_option = " --exposure-mu "
end
local command = enfuse_installed.." --depth "..depth.value..exposure_option..ugly_decimal_point_hack
.." -o \""..output_image.."\" \"@"..response_file.."\""
if dtsys.external_command( command) > 0 then
dt.print(_("Enfuse failed, see terminal output for details"))
os.remove(response_file)
return
end
-- remove the response file
os.remove(response_file)
-- import resulting tiff
local image = dt.database.import(output_image)
-- tell the user that everything worked
dt.print(_("enfuse was successful, resulting image has been imported"))
-- normally printing to stdout is bad, but we allow enfuse to show its output, so adding one extra line is ok
print(string.format(_("enfuse: done, resulting image '%s' has been imported with id %d"), output_image, image.id))
-- initialize exposure_mu value and depth setting in config to sane defaults (would be 0 otherwise)
if dt.preferences.read("enfuse", "depth", "integer") == 0 then
dt.preferences.write("enfuse", "depth", "integer", 2)
dt.preferences.write("enfuse", "exposure_mu", "float", 0.5)
dt.preferences.write("enfuse", "exposure_optimum", "float", 0.5)
end
}
local lib_widgets = {}
-- set up some widgets, initialized from config
if not enfuse_installed then
table.insert(lib_widgets, df.executable_path_widget({"ffmpeg"}))
end
table.insert(lib_widgets, exposure_mu)
table.insert(lib_widgets, depth)
table.insert(lib_widgets, enfuse_button)
local exposure_mu = nil
-- ... and tell dt about it all
dt.register_lib(
"enfuse", -- plugin name
"enfuse", -- name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
dt.new_widget("box") -- widget
if version < "4.2" then
exposure_mu = dt.new_widget("slider")
{
label = "exposure mu",
tooltip = "center also known as MEAN of Gaussian weighting function (0 <= MEAN <= 1); default: 0.5",
hard_min = 0,
hard_max = 1,
value = dt.preferences.read("enfuse", "exposure_mu", "float")
}
else
exposure_mu = dt.new_widget("slider")
{
label = "exposure optimum",
tooltip = "optimum exposure value, usually the maximum of the weighting function (0 <= OPTIMUM <=1); default 0.5",
hard_min = 0,
hard_max = 1,
value = dt.preferences.read("enfuse", "exposure_optimum", "float")
}
end
local depth = dt.new_widget("combobox")
{
orientation = "vertical",
sensitive = enfuse_installed,
table.unpack(lib_widgets)
},
nil,-- view_enter
nil -- view_leave
)
label = "depth",
tooltip = "the number of bits per channel of the output image",
value = dt.preferences.read("enfuse", "depth", "integer"),
changed_callback = function(w) dt.preferences.write("enfuse", "depth", "integer", w.selected) end,
"8", "16", "32"
}
local blend_colorspace = dt.new_widget("combobox")
{
label = "blend colorspace",
tooltip = "Force blending in selected colorspace",
changed_callback = function(w) dt.preferences.write("enfuse", "blend_colorspace", "string", w.selected) end,
"", "identity", "ciecam"
}
local enfuse_button = dt.new_widget("button")
{
label = enfuse_installed and "run enfuse" or "enfuse not installed",
clicked_callback = function ()
-- remember exposure_mu
-- TODO: find a way to save it whenever the value changes
local mu = exposure_mu.value
if version < "4.2" then
dt.preferences.write("enfuse", "exposure-mu", "float", mu)
else
dt.preferences.write("enfuse", "exposure-optimum", "float", mu)
end
-- create a temp response file
local response_file = os.tmpname()
if dt.configuration.running_os == "windows" then
response_file = dt.configuration.tmp_dir .. response_file -- windows os.tmpname() defaults to root directory
end
local f = io.open(response_file, "w")
if not f then
dt.print(string.format(_("Error writing to `%s`"), response_file))
os.remove(response_file)
return
end
-- add all filenames to the response file
local cnt = 0
local n_skipped = 0
local target_dir
for i_, i in ipairs(dt.gui.action_images) do
-- only use ldr files as enfuse can't open raws
if i.is_ldr then
cnt = cnt + 1
f:write(i.path..PS..i.filename.."\n")
target_dir = i.path
-- alternatively raws will be exported as tiff
elseif i.is_raw then
local tmp_exported = os.tmpname()..".tif"
if dt.configuration.running_os == "windows" then
tmp_exported = dt.configuration.tmp_dir .. tmp_exported -- windows os.tmpname() defaults to root directory
end
dt.print(string.format(_("Converting raw file '%s' to tiff..."), i.filename))
tiff_exporter:write_image(i, tmp_exported, false)
dt.print_log(string.format("Raw file '%s' converted to '%s'", i.filename, tmp_exported))
cnt = cnt + 1
f:write(tmp_exported.."\n")
target_dir = i.path
-- other images will be skipped
else
dt.print(string.format(_("Skipping %s..."), i.filename))
n_skipped = n_skipped + 1
end
end
f:close()
-- bail out if there is nothing to do
if cnt == 0 then
dt.print(_("No suitable images selected, nothing to do for enfuse"))
os.remove(response_file)
return
end
if n_skipped > 0 then
dt.print(string.format(_("%d image(s) skipped"), n_skipped))
end
-- call enfuse on the response file
-- TODO: find something nicer
local ugly_decimal_point_hack = string.gsub(string.format("%.04f", mu), ",", ".")
local output_image_date = os.date("%Y%m%d%H%M%S")
local output_image = target_dir.. PS .. "enfuse-"..output_image_date..".tif"
local exposure_option = " --exposure-optimum "
local blend_colorspace_option = ""
if #blend_colorspace.value > 1 then
blend_colorspace_option = " --blend-colorspace="..blend_colorspace.value
end
if version < "4.2" then
exposure_option = " --exposure-mu "
end
local command = enfuse_installed.." --depth "..depth.value..exposure_option..ugly_decimal_point_hack
..blend_colorspace_option
.." -o \""..output_image.."\" \"@"..response_file.."\""
if dtsys.external_command( command) > 0 then
dt.print(_("Enfuse failed, see terminal output for details"))
os.remove(response_file)
return
end
-- remove the response file
os.remove(response_file)
-- import resulting tiff
local image = dt.database.import(output_image)
-- tell the user that everything worked
dt.print(_("enfuse was successful, resulting image has been imported"))
-- normally printing to stdout is bad, but we allow enfuse to show its output, so adding one extra line is ok
print(string.format(_("enfuse: done, resulting image '%s' has been imported with id %d"), output_image, image.id))
end
}
local lib_widgets = {}
if not enfuse_installed then
table.insert(enf.lib_widgets, df.executable_path_widget({"ffmpeg"}))
end
table.insert(enf.lib_widgets, exposure_mu)
table.insert(enf.lib_widgets, depth)
table.insert(enf.lib_widgets, blend_colorspace)
table.insert(enf.lib_widgets, enfuse_button)
-- ... and tell dt about it all
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not enf.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
)
enf.event_registered = true
end
end
else
dt.print_error("enfuse executable not found")
error("enfuse executable not found")
dt.print(_("Could not find enfuse executable. Not loading enfuse exporter..."))
end
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua;

View File

@@ -37,9 +37,9 @@ USAGE
local dt = require "darktable"
local du = require "lib/dtutils"
require "darktable.debug"
require "official/yield"
du.check_min_api_version("2.1.0", "generate_image_txt")
local CURR_API_STRING = dt.configuration.api_version_string
dt.preferences.register("generate_image_txt",
"enabled",
@@ -66,7 +66,8 @@ end
local command_setting = dt.preferences.read("generate_image_txt", "command", "string")
check_command(command_setting)
dt.register_event("mouse-over-image-changed", function(event, img)
dt.register_event("mouse-over-image-changed",
function(event, img)
-- no need to waste processing time if the image has a txt file already
if not img or img.has_txt or not dt.preferences.read("generate_image_txt", "enabled", "bool") then
return

View File

@@ -32,9 +32,24 @@ local dt = require "darktable"
local du = require "lib/dtutils"
du.check_min_api_version("2.0.0", "image_path_in_ui")
local CURR_API_STRING = dt.configuration.api_version_string
local ipiu = {}
ipiu.module_installed = false
ipiu.event_registered = false
local main_label = dt.new_widget("label"){selectable = true, ellipsize = "middle", halign = "start"}
local function install_module()
if not ipiu.module_installed then
dt.register_lib("image_path_no_ui", "selected images path", true, false,{
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 300}
}, main_label
)
ipiu.module_installed = true
end
end
local function reset_widget()
local selection = dt.gui.selection()
local result = ""
@@ -54,12 +69,23 @@ end
main_label.reset_callback = reset_widget
dt.register_lib("image_path_no_ui","Selected Images path",true,false,{
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER",300}
}, main_label
);
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not ipiu.event_registered then
dt.register_event(
"ipiu", "view-changed",
function(event, old_view, new_view)
if new_view.name == "lighttable" and old_view.name == "darkroom" then
install_module()
end
end
)
ipiu.event_registered = true
end
end
dt.register_event("mouse-over-image-changed",reset_widget);
dt.register_event("mouse-over-image-changed", reset_widget);
--
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua

View File

@@ -34,6 +34,7 @@ local dt = require "darktable"
local import_filter_list = {}
local n_import_filters = 1
local CURR_API_STRING = dt.configuration.api_version_string
-- allow changing the filter from the preferences
dt.preferences.register("import_filter_manager", "active_filter", "string",

View File

@@ -37,21 +37,22 @@ local dt = require "darktable"
local du = require "lib/dtutils"
du.check_min_api_version("2.0.0", "save_selection")
local CURR_API_STRING = dt.configuration.api_version_string
local buffer_count = 5
for i=1,buffer_count do
local saved_selection
dt.register_event("shortcut",function()
dt.register_event("shortcut", function()
saved_selection = dt.gui.selection()
end,"save to buffer "..i)
dt.register_event("shortcut",function()
dt.register_event("shortcut", function()
dt.gui.selection(saved_selection)
end,"restore from buffer "..i)
end
local bounce_buffer = {}
dt.register_event("shortcut",function()
dt.register_event("shortcut", function()
bounce_buffer = dt.gui.selection(bounce_buffer)
end,"switch selection with temporary buffer")

View File

@@ -35,7 +35,6 @@ Plugin allows you to choose how many thumbnails you need per row
]]
local dt = require "darktable"
local du = require "lib/dtutils"
require "official/yield"
du.check_min_api_version("2.0.0", "selection_to_pdf")

View File

@@ -1,42 +0,0 @@
--[[
This file is part of darktable,
Copyright 2016 by Tobias Jakobs.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
darktable yield compatibility script
USAGE
* require this script from your main Lua file in the first line
]]
local dt = require "darktable"
local yield_orig = coroutine.yield
if (dt.configuration.api_version_major < 4) then
dt.control = {}
dt.control.execute = function(command)
yield_orig("RUN_COMMAND", command)
end
dt.control.read = function(command)
yield_orig("FILE_READABLE", command)
end
dt.control.sleep = function(command)
yield_orig("WAIT_MS", command)
end
end
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
-- kate: hl Lua;

View File

@@ -37,9 +37,14 @@ du.check_min_api_version("5.0.0", "executable_manager")
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
local gettext = dt.gettext
local CURR_API_STRING = dt.configuration.api_version_string
gettext.bindtextdomain("executable_manager",dt.configuration.config_dir.."/lua/locale/")
local exec_man = {} -- our own namespace
exec_man.module_installed = false
exec_man.event_registered = false
-- - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- F U N C T I O N S
-- - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -86,12 +91,30 @@ local function update_combobox_choices(combobox, choice_table, selected)
combobox.value = selected
end
local function install_module()
if not exec_man.module_installed then
dt.register_lib(
"executable_manager", -- Module name
"executable manager", -- Visible name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_BOTTOM", 100}}, -- containers
dt.new_widget("box") -- widget
{
orientation = "vertical",
exec_man.selector,
exec_man.stack,
},
nil,-- view_enter
nil -- view_leave
)
exec_man.module_installed = true
end
end
-- - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- M A I N P R O G R A M
-- - - - - - - - - - - - - - - - - - - - - - - - - - - -
local exec_man = {} -- our own namespace
local DARKTABLERC = dt.configuration.config_dir .. PS .. "darktablerc"
@@ -117,8 +140,15 @@ for _,pref in ipairs(matches) do
end
local executable_path_widgets = {}
local executable_path_values = {}
local placeholder_text = dt.configuration.running_os == windows and _("select an executable") or _("search path for executable")
for i,exec in ipairs(exec_table) do
executable_path_values[exec] = dt.new_widget("entry"){
text = df.get_executable_path_preference(exec),
placeholder = placeholder_text,
editable = false
}
executable_path_widgets[exec] = dt.new_widget("file_chooser_button"){
title = _("select ") .. exec .. _(" executable"),
value = df.get_executable_path_preference(exec),
@@ -126,6 +156,7 @@ for i,exec in ipairs(exec_table) do
changed_callback = function(self)
if df.check_if_bin_exists(self.value) then
df.set_executable_path_preference(exec, self.value)
executable_path_values[exec].text = df.get_executable_path_preference(exec)
end
end
}
@@ -155,13 +186,18 @@ exec_man.selector = dt.new_widget("combobox"){
for i,exec in ipairs(exec_table) do
exec_man.stack[i] = dt.new_widget("box"){
dt.new_widget("section_label"){label = _("current")},
executable_path_values[exec],
dt.new_widget("section_label"){label = _("select")},
executable_path_widgets[exec],
dt.new_widget("section_label"){label = _("reset")},
dt.new_widget("button"){
label = "clear",
tooltip = _("Clear path for ") .. exec,
clicked_callback = function()
df.set_executable_path_preference(exec, "")
executable_path_widgets[exec].value = ""
executable_path_values[exec].text = ""
end
}
@@ -174,19 +210,18 @@ update_combobox_choices(exec_man.selector, exec_table, 1)
-- register the lib
dt.register_lib(
"executable_manager", -- Module name
"executable manager", -- Visible name
true, -- expandable
false, -- resetable
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_BOTTOM", 100}}, -- containers
dt.new_widget("box") -- widget
{
orientation = "vertical",
exec_man.selector,
exec_man.stack,
},
nil,-- view_enter
nil -- view_leave
)
if dt.gui.current_view().id == "lighttable" then
install_module()
else
if not exec_man.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
)
exec_man.event_registered = true
end
end

View File

@@ -1 +0,0 @@
/home/bill/src/plugins/lua-scripts/tools/plugin_manager.lua

File diff suppressed because it is too large Load Diff