Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6548a12bf1 | ||
|
|
becfa5350b |
73
ChangeLog.md
73
ChangeLog.md
@@ -1,78 +1,5 @@
|
|||||||
## Changes from most recent to oldest
|
## Changes from most recent to oldest
|
||||||
|
|
||||||
**06 Jun 2024 - wpferguson**
|
|
||||||
* fix fujifilm_ratings running on all images
|
|
||||||
* added string library functions to sanitize windows io.popen and os.execute functions
|
|
||||||
* added database maintenance script
|
|
||||||
|
|
||||||
**05 Jun 2024 - wpferguson**
|
|
||||||
* added fix for executable_manager not being visible on windows
|
|
||||||
|
|
||||||
**30 May 2024 - kkotowicz**
|
|
||||||
* open in explorer now uses applescript on macos to open multiple files
|
|
||||||
|
|
||||||
**20 May 2024 - wpferguson**
|
|
||||||
* added string variable substitution to the string library
|
|
||||||
|
|
||||||
**16 May 2024 - wpferguson**
|
|
||||||
* fix crash in script_manager
|
|
||||||
|
|
||||||
**15 May 2024 - wpferguson**
|
|
||||||
* added metadata to scripts (name, author, purpose, help url)
|
|
||||||
|
|
||||||
**06 May 2024 - christian.sueltrop**
|
|
||||||
* added passport_guide_germany script
|
|
||||||
|
|
||||||
**08 Apr 2024 - wpferguson**
|
|
||||||
* made script_manager aware of library modules in other directories besides lib
|
|
||||||
|
|
||||||
**29 Mar 2024 - wpferguson**
|
|
||||||
* updated examples/gui_action to use NaN instead of nan
|
|
||||||
|
|
||||||
**29 Mar 2024 - dterrahe**
|
|
||||||
* add lua action script example
|
|
||||||
|
|
||||||
**28 Jan 2024 - wpferguson**
|
|
||||||
* fix script_manager crash when script existed in top level lua directory
|
|
||||||
|
|
||||||
**24 Jan 2024 - wpferguson**
|
|
||||||
* don't set lib visibility unless we are in lighttable mode
|
|
||||||
|
|
||||||
**15 Jan 2024 - MStraeten**
|
|
||||||
* update x-touch.lua with support for primaries slider
|
|
||||||
|
|
||||||
**14 Jan 2024 - wpferguson**
|
|
||||||
* added cycle_group_leader script
|
|
||||||
* added hif_group_leader script
|
|
||||||
* added jpg_group_leader script
|
|
||||||
|
|
||||||
**28 Oct 2023 - ddittmar**
|
|
||||||
* Added select non existing image script
|
|
||||||
|
|
||||||
**17 Oct 2023 - wpferguson**
|
|
||||||
* script_manager wrap username in quotes to handle spaces in username
|
|
||||||
|
|
||||||
**20 Sep 2023 - wpferguson**
|
|
||||||
* script_manager explicitly set stopped scripts to false
|
|
||||||
|
|
||||||
**18 Aug 2023 - wpferguson**
|
|
||||||
* swap the position of executable_manager and script_manager due to windows gtk bug
|
|
||||||
|
|
||||||
**17 Jul 2023 - wpferguson**
|
|
||||||
* update check for update instructions
|
|
||||||
* update flatpak readme
|
|
||||||
|
|
||||||
**16 Jul 2023 - wpferguson**
|
|
||||||
* added script_data.show function to contrib/gpx_export
|
|
||||||
|
|
||||||
**15 Jul 2023 - wpferguson**
|
|
||||||
* script_manager updates
|
|
||||||
* added check_max_api_version for the case where the API no longer supports the required
|
|
||||||
functions
|
|
||||||
|
|
||||||
**14 Jul 2023 - spaceChRis**
|
|
||||||
* add tooltip to filter manager
|
|
||||||
|
|
||||||
**25 Mar 2023 - wpferguson**
|
**25 Mar 2023 - wpferguson**
|
||||||
* Added script_manager darktable shortcut integration
|
* Added script_manager darktable shortcut integration
|
||||||
* Moved filename/path/extension string functions to string library
|
* Moved filename/path/extension string functions to string library
|
||||||
|
|||||||
149
README.md
149
README.md
@@ -15,17 +15,17 @@ These scripts are written primarily by the darktable developers and maintained b
|
|||||||
|
|
||||||
Name|Standalone|OS |Purpose
|
Name|Standalone|OS |Purpose
|
||||||
----|:--------:|:---:|-------
|
----|:--------:|:---:|-------
|
||||||
[check_for_updates](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/check_for_updates)|Yes|LMW|Check for updates to darktable
|
[check_for_updates](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/check_for_updates)|Yes|LMW|Check for updates to darktable
|
||||||
[copy_paste_metadata](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/copy_paste_metadata)|Yes|LMW|Copy and paste metadata, tags, ratings, and color labels between images
|
[copy_paste_metadata](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/copy_paste_metadata)|Yes|LMW|Copy and paste metadata, tags, ratings, and color labels between images
|
||||||
[delete_long_tags](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/delete_long_tags)|Yes|LMW|Delete all tags longer than a specified length
|
[delete_long_tags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/delete_long_tags)|Yes|LMW|Delete all tags longer than a specified length
|
||||||
[delete_unused_tags](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/delete_unused_tags)|Yes|LMW|Delete tags that have no associated images
|
[delete_unused_tags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/delete_unused_tags)|Yes|LMW|Delete tags that have no associated images
|
||||||
[enfuse](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/enfuse)|No|L|Exposure blend several images (HDR)
|
[enfuse](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/enfuse)|No|L|Exposure blend several images (HDR)
|
||||||
[generate_image_txt](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/generate_image_txt)|No|L|Generate txt sidecar files to be overlaid on zoomed images
|
[generate_image_txt](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/generate_image_txt)|No|L|Generate txt sidecar files to be overlaid on zoomed images
|
||||||
[image_path_in_ui](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/image_path_in_ui)|Yes|LMW|Plugin to display selected image path
|
[image_path_in_ui](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/image_path_in_ui)|Yes|LMW|Plugin to display selected image path
|
||||||
[import_filter_manager](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/import_filter_manager)|Yes|LMW|Manager for import filters
|
[import_filter_manager](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/import_filter_manager)|Yes|LMW|Manager for import filters
|
||||||
[import_filters](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/import_filters)|No|LMW|Two import filters for use with import_filter_manager
|
[import_filters](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/import_filters)|No|LMW|Two import filters for use with import_filter_manager
|
||||||
[save_selection](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/save_selection)|Yes|LMW|Provide save and restore from multiple selection buffers
|
[save_selection](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/save_selection)|Yes|LMW|Provide save and restore from multiple selection buffers
|
||||||
[selection_to_pdf](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/selection_to_pdf)|No|L|Generate a PDF file from the selected images
|
[selection_to_pdf](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/official/selection_to_pdf)|No|L|Generate a PDF file from the selected images
|
||||||
|
|
||||||
|
|
||||||
### Contributed Scripts
|
### Contributed Scripts
|
||||||
@@ -34,52 +34,43 @@ These scripts are contributed by users. They are meant to have an "owner", i.e.
|
|||||||
|
|
||||||
Name|Standalone|OS |Purpose
|
Name|Standalone|OS |Purpose
|
||||||
----|:--------:|:---:|-------
|
----|:--------:|:---:|-------
|
||||||
[AutoGrouper](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/autogrouper)|Yes|LMW|Group images together by time
|
[AutoGrouper](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/autogrouper)|Yes|LMW|Group images together by time
|
||||||
[autostyle](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/autostyle)|Yes|LMW|Automatically apply styles on import
|
[autostyle](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/autostyle)|Yes|LMW|Automatically apply styles on import
|
||||||
[auto_tag_modified](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/auto_tag_modified)|Yes|LMW|Automatically tag images which have been manually modified in the darkroom
|
|
||||||
change_group_leader|Yes|LMW|Change which image leads group
|
change_group_leader|Yes|LMW|Change which image leads group
|
||||||
[clear_GPS](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/clear_gps)|Yes|LMW|Reset GPS information for selected images
|
[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://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/collecthelper)|Yes|LMW|Add buttons to selected images module to manipulate the collection
|
[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
|
||||||
color_profile_manager|Yes|LMW|Manage darktable input and output color profiles
|
[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
|
||||||
[copy_attach_detach_tags](https://docs.darktable.org/lua/stable/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
|
||||||
[cr2hdr](https://docs.darktable.org/lua/stable/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
|
||||||
cycle_group_leader|Yes|LMW|cycle through images of a group making each the group leader in turn
|
[exportLUT](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/exportlut)|Yes|LMW|Create a LUT from a style and export it
|
||||||
dbmaint|Yes|LMW|find and remove database entries for missing film rolls and images
|
[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
|
||||||
[enfuseAdvanced](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/enfuseadvanced)|No|LMW|Merge multiple images into Dynamic Range Increase (DRI) or Depth From Focus (DFF) images
|
[face_recognition](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/face_recognition)|No|LM|Identify and tag images using facial recognition
|
||||||
[exportLUT](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/exportlut)|Yes|LMW|Create a LUT from a style and export it
|
[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
|
||||||
[ext_editor](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/ext_editor)|No|LW|Export pictures to collection and edit them with up to nine user-defined external editors
|
[fujifilm_ratings](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/fujifilm_ratings)|No|LM|Support importing Fujifilm ratings
|
||||||
[face_recognition](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/face_recognition)|No|LM|Identify and tag images using facial recognition
|
[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 ...
|
||||||
[fujifilm_dynamic_range](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/fujifilm_dynamic_range)|No|LMW|Correct fujifilm exposure based on exposure bias camera setting
|
[geoToolbox](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/geotoolbox)|No|LMW|A toolbox of geo functions
|
||||||
[fujifilm_ratings](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/fujifilm_ratings)|No|LM|Support importing Fujifilm ratings
|
[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
|
||||||
[geoJSON_export](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/geojson_export)|No|L|Create a geo JSON script with thumbnails for use in ...
|
[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
|
||||||
[geoToolbox](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/geotoolbox)|No|LMW|A toolbox of geo functions
|
[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
|
||||||
[gimp](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/gimp)|No|LMW|Open an image in GIMP for editing 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
|
||||||
[gpx_export](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/gpx_export)|No|LMW|Export a GPX track file from selected images GPS data
|
[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
|
||||||
harmonic_armature|Yes|LMW|provide a harmonic armature guide
|
[image_time](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/image_time)|Yes|LMW|Adjust the EXIF image time
|
||||||
hif_group_leader|Yes|LMW|change the group leader in a raw+heif image pair to the heif image
|
[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
|
||||||
[HDRMerge](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/hdrmerge)|No|LMW|Combine the selected images into an HDR DNG and return the result
|
[LabelsToTags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/labelstotags)|Yes|LMW|Apply tags based on color labels and ratings
|
||||||
[hugin](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/hugin)|No|LMW|Combine selected images into a panorama and return the result
|
[OpenInExplorer](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/openinexplorer)|No|LMW|Open the selected images in the system file manager
|
||||||
[image_stack](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/image_stack)|No|LMW|Combine a stack of images to remove noise or transient objects
|
[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
|
||||||
[image_time](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/image_time)|Yes|LMW|Adjust the EXIF image time
|
[pdf_slideshow](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/pdf_slideshow)|No|LM|Export images to a PDF slideshow
|
||||||
jpg_group_leader|Yes|LMW|change the group leader for a raw+jpg pair to the jpg image
|
[photils](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/photils)|No|LM|Automatic tag suggestions for your images
|
||||||
[kml_export](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/kml_export)|No|L|Export photos with a KML file for usage in Google Earth
|
[quicktag](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/quicktag)|Yes|LMW|Create shortcuts for quickly applying tags
|
||||||
[LabelsToTags](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/labelstotags)|Yes|LMW|Apply tags based on color labels and ratings
|
[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
|
||||||
[OpenInExplorer](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/openinexplorer)|No|LMW|Open the selected images in the system file manager
|
[rename_images](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/rename_images)|Yes|LMW|Rename single or multiple images
|
||||||
[passport_guide](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/passport_guide)|Yes|LMW|Add passport cropping guide to darkroom crop tool
|
[rename-tags](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/rename-tags)|Yes|LMW|Change a tag name
|
||||||
passport_guide_germany|Yes|LMW|Add passport cropping guide for German passports to darkroom crop tool
|
[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)
|
||||||
[pdf_slideshow](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/pdf_slideshow)|No|LM|Export images to a PDF slideshow
|
[select_non_existing](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/select_non_existing)|Yes|LMW|Enable selection of non-existing images in the the currently worked on images
|
||||||
[photils](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/photils)|No|LM|Automatic tag suggestions for your images
|
[select_untagged](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/select_untagged)|Yes|LMW|Enable selection of untagged images
|
||||||
[quicktag](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/quicktag)|Yes|LMW|Create shortcuts for quickly applying tags
|
[slideshowMusic](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/slideshowMusic)|No|L|Play music during a slideshow
|
||||||
[rate_group](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/rate_group)|Yes|LMW|Apply or remove a star rating from grouped images
|
[transfer_hierarchy](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/transfer_hierarchy)|Yes|LMW|Image move/copy preserving directory hierarchy
|
||||||
[rename_images](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/rename_images)|Yes|LMW|Rename single or multiple images
|
[video_ffmpeg](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/contrib/video_ffmpeg)|No|LMW|Export video from darktable
|
||||||
[rename-tags](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/rename-tags)|Yes|LMW|Change a tag name
|
|
||||||
[RL_out_sharp](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/rl_out_sharp)|No|LW|Output sharpening using GMic (Richardson-Lucy algorithm)
|
|
||||||
[select_non_existing](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/select_non_existing)|Yes|LMW|Enable selection of non-existing images in the the currently worked on images
|
|
||||||
[select_untagged](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/select_untagged)|Yes|LMW|Enable selection of untagged images
|
|
||||||
[slideshowMusic](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/slideshowMusic)|No|L|Play music during a slideshow
|
|
||||||
[transfer_hierarchy](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/transfer_hierarchy)|Yes|LMW|Image move/copy preserving directory hierarchy
|
|
||||||
[video_ffmpeg](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/video_ffmpeg)|No|LMW|Export video from darktable
|
|
||||||
website_gallery_export|No|LMW|Export a website gallery for selected images
|
|
||||||
|
|
||||||
### Example Scripts
|
### Example Scripts
|
||||||
|
|
||||||
@@ -87,19 +78,17 @@ These scripts provide examples of how to use specific portions of the API. They
|
|||||||
|
|
||||||
Name|Standalone|OS |Purpose
|
Name|Standalone|OS |Purpose
|
||||||
----|:--------:|:---:|-------
|
----|:--------:|:---:|-------
|
||||||
[api_version](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/api_version)|Yes|LMW|Print the current API version
|
[api_version](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/api_version)|Yes|LMW|Print the current API version
|
||||||
[darkroom_demo](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/darkroom_demo)|Yes|LMW|Demonstrate changing images in darkoom
|
[darkroom_demo](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/darkroom_demo)|Yes|LMW|Demonstrate changing images in darkoom
|
||||||
[gettextExample](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/gettextexample)|Yes|LM|How to use translation
|
[gettextExample](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/gettextexample)|Yes|LM|How to use translation
|
||||||
gui_actions|Yes|LMW|demonstrate controlling the GUI using darktable.gui.action calls
|
[hello_world](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/examples/hello_world)|Yes|LMW|Prints hello world when darktable starts
|
||||||
[hello_world](https://docs.darktable.org/lua/stable/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
|
||||||
[lighttable_demo](https://docs.darktable.org/lua/stable/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
|
||||||
[moduleExample](https://docs.darktable.org/lua/stable/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
|
||||||
[multi_os](https://docs.darktable.org/lua/stable/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
|
||||||
[panels_demo](https://docs.darktable.org/lua/stable/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
|
||||||
[preferenceExamples](https://docs.darktable.org/lua/stable/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
|
||||||
[printExamples](https://docs.darktable.org/lua/stable/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
|
||||||
[running_os](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/running_os)|Yes|LMW|Print out the running operating system
|
|
||||||
x-touch|Yes|LMW|demonstrate how to use an x-touch mini MIDI controller to control the darktable GUI
|
|
||||||
|
|
||||||
### Tools
|
### Tools
|
||||||
|
|
||||||
@@ -107,11 +96,11 @@ Tool scripts perform functions relating to the repository, such as generating do
|
|||||||
|
|
||||||
Name|Standalone|OS |Purpose
|
Name|Standalone|OS |Purpose
|
||||||
----|:--------:|:---:|-------
|
----|:--------:|:---:|-------
|
||||||
[executable_manager](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/tools/executable_manager)|Yes|LMW|Manage the external executables used by the lua scripts
|
[executable_manager](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/executable_manager)|Yes|LMW|Manage the external executables used by the lua scripts
|
||||||
[gen_i18n_mo](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/tools/gen_i18n_mo)|No|LMW|Generate compiled translation files (.mo) from source files (.po)
|
[gen_i18n_mo](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/gen_i18n_mo)|No|LMW|Generate compiled translation files (.mo) from source files (.po)
|
||||||
[get_lib_manpages](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/tools/get_lib_manpages)|No|LM|Retrieve the library documentation and output it in man page and PDF format
|
[get_lib_manpages](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/get_lib_manpages)|No|LM|Retrieve the library documentation and output it in man page and PDF format
|
||||||
[get_libdoc](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/tools/get_libdoc)|No|LMW|Retrieve the library documentation and output it as text
|
[get_libdoc](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/get_libdoc)|No|LMW|Retrieve the library documentation and output it as text
|
||||||
[script_manager](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/tools/script_manager)|No|LMW|Manage (install, update, enable, disable) the lua scripts
|
[script_manager](https://darktable-org.github.io/luadocs/lua.scripts.manual/scripts/tools/script_manager)|No|LMW|Manage (install, update, enable, disable) the lua scripts
|
||||||
|
|
||||||
### Related third-party projects
|
### Related third-party projects
|
||||||
|
|
||||||
@@ -129,8 +118,6 @@ The following third-party projects are listed for information only. Think of thi
|
|||||||
* [nbremond77/darktable](https://github.com/nbremond77/darktable/tree/master/scripts)
|
* [nbremond77/darktable](https://github.com/nbremond77/darktable/tree/master/scripts)
|
||||||
* [s5k6/dtscripts](https://github.com/s5k6/dtscripts)
|
* [s5k6/dtscripts](https://github.com/s5k6/dtscripts)
|
||||||
* [ChristianBirzer/darktable_extra_scripts](https://github.com/ChristianBirzer/darktable_extra_scripts)
|
* [ChristianBirzer/darktable_extra_scripts](https://github.com/ChristianBirzer/darktable_extra_scripts)
|
||||||
* [fjb2020/darktable-scripts](https://github.com//fjb2020/darktable-scripts)
|
|
||||||
* [bastibe/Darktable-Film-Simulation-Panel](https://github.com/bastibe/Darktable-Film-Simulation-Panel)
|
|
||||||
|
|
||||||
## Download and Install
|
## Download and Install
|
||||||
|
|
||||||
@@ -180,7 +167,7 @@ If you don't have %LOCALAPPDATA%\darktable you have to start dartable at least o
|
|||||||
|
|
||||||
When darktable starts it looks for a file name `~/.config/darktable/luarc` (`%LOCALAPPDATA%\darktable\luarc` for windows) and reads it to see which scripts to include. The file is a plain text file with entries of the form `require "<directory>/<name>"` where directory is the directory containing the scripts, from the above list, and name is the name from the above list. To include GIMP the line would be `require "contrib/gimp"`.
|
When darktable starts it looks for a file name `~/.config/darktable/luarc` (`%LOCALAPPDATA%\darktable\luarc` for windows) and reads it to see which scripts to include. The file is a plain text file with entries of the form `require "<directory>/<name>"` where directory is the directory containing the scripts, from the above list, and name is the name from the above list. To include GIMP the line would be `require "contrib/gimp"`.
|
||||||
|
|
||||||
The recommended way to enable and disable specific scripts is using the [script manager](https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/tools/script_manager/) module. To use script manager do the following:
|
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
|
### Linux or MacOS
|
||||||
|
|
||||||
@@ -239,18 +226,18 @@ To update the script repository, open a terminal or command prompt and do the fo
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The [Lua Scripts Manual](https://docs.darktable.org/lua/stable/lua.scripts.manual/) provides documentation
|
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
|
for the scripts transcribed from the header comments. Each script also contains comments and usage instructions
|
||||||
in the header comments.
|
in the header comments.
|
||||||
|
|
||||||
The [Lua Scripts Library API Manual](https://docs.darktable.org/lua/stable/lua.scripts.api.manual/) provides
|
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
|
documentation of the libraries and functions. Lua-script libraries documentation may also be generated using
|
||||||
the tools in the tools/ directory.
|
the tools in the tools/ directory.
|
||||||
|
|
||||||
More information about the scripting with Lua can be found in the darktable user manual:
|
More information about the scripting with Lua can be found in the darktable user manual:
|
||||||
[Scripting with Lua](https://darktable.org.github.io/dtdocs/lua/)
|
[Scripting with Lua](https://darktable-org.github.io/dtdocs/lua/)
|
||||||
|
|
||||||
The [Lua API Manual](https://docs.darktable.org/lua/stable/lua.api.manual/) provides docuemntation of the
|
The [Lua API Manual](https://darktable-org.github.io/luadocs/lua.api.manual/) provides docuemntation of the
|
||||||
darktable Lua API.
|
darktable Lua API.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|||||||
@@ -1,225 +1,219 @@
|
|||||||
--[[AutoGrouper plugin for darktable
|
--[[AutoGrouper plugin for darktable
|
||||||
|
|
||||||
copyright (c) 2019 Kevin Ertel
|
copyright (c) 2019 Kevin Ertel
|
||||||
|
|
||||||
darktable is free software: you can redistribute it and/or modify
|
darktable is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
darktable is distributed in the hope that it will be useful,
|
darktable is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
--[[About this Plugin
|
--[[About this Plugin
|
||||||
This plugin adds the module "Auto Group" to darktable's lighttable view
|
This plugin adds the module "Auto Group" to darktable's lighttable view
|
||||||
|
|
||||||
----REQUIRED SOFTWARE----
|
----REQUIRED SOFTWARE----
|
||||||
None
|
None
|
||||||
|
|
||||||
----USAGE----
|
----USAGE----
|
||||||
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts )
|
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts )
|
||||||
1) Copy this file in to your "lua/contrib" folder where all other scripts reside.
|
1) Copy this file 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
|
2) Require this file in your luarc file, as with any other dt plug-in
|
||||||
|
|
||||||
Set a gap amount in second which will be used to determine when images should no
|
Set a gap amount in second which will be used to determine when images should no
|
||||||
longer be added to a group. If an image is more then the specified amount of time
|
longer be added to a group. If an image is more then the specified amount of time
|
||||||
from the last image in the group it will not be added. Images without timestamps
|
from the last image in the group it will not be added. Images without timestamps
|
||||||
in exif data will be ignored.
|
in exif data will be ignored.
|
||||||
|
|
||||||
There are two buttons. One allows the grouping to be performed only on the currently
|
There are two buttons. One allows the grouping to be performed only on the currently
|
||||||
selected images, the other button performs grouping on the entire active collection
|
selected images, the other button performs grouping on the entire active collection
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "AutoGrouper")
|
du.check_min_api_version("7.0.0", "AutoGrouper")
|
||||||
|
|
||||||
local MOD = 'autogrouper'
|
local MOD = 'autogrouper'
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local function _(msgid)
|
local script_data = {}
|
||||||
return gettext(msgid)
|
|
||||||
end
|
script_data.destroy = nil -- function to destory the script
|
||||||
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
-- return data structure for script_manager
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
local script_data = {}
|
|
||||||
|
local gettext = dt.gettext
|
||||||
script_data.metadata = {
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
name = _("auto group"),
|
gettext.bindtextdomain("AutoGrouper",dt.configuration.config_dir.."/lua/locale/")
|
||||||
purpose = _("automatically group images by time interval"),
|
|
||||||
author = "Kevin Ertel",
|
local function _(msgid)
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/AutoGrouper/"
|
return gettext.dgettext("AutoGrouper", msgid)
|
||||||
}
|
end
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
local Ag = {}
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
Ag.module_installed = false
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
Ag.event_registered = false
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
local GUI = {
|
||||||
local Ag = {}
|
gap = {},
|
||||||
Ag.module_installed = false
|
selected = {},
|
||||||
Ag.event_registered = false
|
collection = {}
|
||||||
|
}
|
||||||
local GUI = {
|
|
||||||
gap = {},
|
|
||||||
selected = {},
|
local function InRange(test, low, high) --tests if test value is within range of low and high (inclusive)
|
||||||
collection = {}
|
if test >= low and test <= high then
|
||||||
}
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
local function InRange(test, low, high) --tests if test value is within range of low and high (inclusive)
|
end
|
||||||
if test >= low and test <= high then
|
end
|
||||||
return true
|
|
||||||
else
|
local function CompTime(first, second) --compares the timestamps and returns true if first was taken before second
|
||||||
return false
|
first_time = first.exif_datetime_taken
|
||||||
end
|
if string.match(first_time, '[0-9]') == nil then first_time = '9999:99:99 99:99:99' end
|
||||||
end
|
first_time = tonumber(string.gsub(first_time, '[^0-9]*',''))
|
||||||
|
second_time = second.exif_datetime_taken
|
||||||
local function CompTime(first, second) --compares the timestamps and returns true if first was taken before second
|
if string.match(second_time, '[0-9]') == nil then second_time = '9999:99:99 99:99:99' end
|
||||||
first_time = first.exif_datetime_taken
|
second_time = tonumber(string.gsub(second_time, '[^0-9]*',''))
|
||||||
if string.match(first_time, '[0-9]') == nil then first_time = '9999:99:99 99:99:99' end
|
return first_time < second_time
|
||||||
first_time = tonumber(string.gsub(first_time, '[^0-9]*',''))
|
end
|
||||||
second_time = second.exif_datetime_taken
|
|
||||||
if string.match(second_time, '[0-9]') == nil then second_time = '9999:99:99 99:99:99' end
|
local function SeperateTime(str) --seperates the timestamp into individual components for used with OS.time operations
|
||||||
second_time = tonumber(string.gsub(second_time, '[^0-9]*',''))
|
local cleaned = string.gsub(str, '[^%d]',':')
|
||||||
return first_time < second_time
|
cleaned = string.gsub(cleaned, '::*',':') --YYYY:MM:DD:hh:mm:ss
|
||||||
end
|
local year = string.sub(cleaned,1,4)
|
||||||
|
local month = string.sub(cleaned,6,7)
|
||||||
local function SeperateTime(str) --seperates the timestamp into individual components for used with OS.time operations
|
local day = string.sub(cleaned,9,10)
|
||||||
local cleaned = string.gsub(str, '[^%d]',':')
|
local hour = string.sub(cleaned,12,13)
|
||||||
cleaned = string.gsub(cleaned, '::*',':') --YYYY:MM:DD:hh:mm:ss
|
local min = string.sub(cleaned,15,16)
|
||||||
local year = string.sub(cleaned,1,4)
|
local sec = string.sub(cleaned,18,19)
|
||||||
local month = string.sub(cleaned,6,7)
|
return {year = year, month = month, day = day, hour = hour, min = min, sec = sec}
|
||||||
local day = string.sub(cleaned,9,10)
|
end
|
||||||
local hour = string.sub(cleaned,12,13)
|
|
||||||
local min = string.sub(cleaned,15,16)
|
local function GetTimeDiff(curr_image, prev_image) --returns the time difference (in sec.) from current image and the previous image
|
||||||
local sec = string.sub(cleaned,18,19)
|
local curr_time = SeperateTime(curr_image.exif_datetime_taken)
|
||||||
return {year = year, month = month, day = day, hour = hour, min = min, sec = sec}
|
local prev_time = SeperateTime(prev_image.exif_datetime_taken)
|
||||||
end
|
return os.time(curr_time)-os.time(prev_time)
|
||||||
|
end
|
||||||
local function GetTimeDiff(curr_image, prev_image) --returns the time difference (in sec.) from current image and the previous image
|
|
||||||
local curr_time = SeperateTime(curr_image.exif_datetime_taken)
|
local function main(on_collection)
|
||||||
local prev_time = SeperateTime(prev_image.exif_datetime_taken)
|
local images = {}
|
||||||
return os.time(curr_time)-os.time(prev_time)
|
if on_collection then
|
||||||
end
|
local col_images = dt.collection
|
||||||
|
for i,image in ipairs(col_images) do --copy images to a standard table, table.sort barfs on type dt_lua_singleton_image_collection
|
||||||
local function main(on_collection)
|
table.insert(images,i,image)
|
||||||
local images = {}
|
end
|
||||||
if on_collection then
|
else
|
||||||
local col_images = dt.collection
|
images = dt.gui.selection()
|
||||||
for i,image in ipairs(col_images) do --copy images to a standard table, table.sort barfs on type dt_lua_singleton_image_collection
|
end
|
||||||
table.insert(images,i,image)
|
dt.preferences.write(MOD, 'active_gap', 'integer', GUI.gap.value)
|
||||||
end
|
if #images < 2 then
|
||||||
else
|
dt.print('please select at least 2 images')
|
||||||
images = dt.gui.selection()
|
return
|
||||||
end
|
end
|
||||||
dt.preferences.write(MOD, 'active_gap', 'integer', GUI.gap.value)
|
table.sort(images, function(first, second) return CompTime(first,second) end) --sort images by timestamp
|
||||||
if #images < 2 then
|
|
||||||
dt.print('please select at least 2 images')
|
for i, image in ipairs(images) do
|
||||||
return
|
if i == 1 then
|
||||||
end
|
prev_image = image
|
||||||
table.sort(images, function(first, second) return CompTime(first,second) end) --sort images by timestamp
|
elseif string.match(image.exif_datetime_taken, '[%d]') ~= nil then --make sure current image has a timestamp, if so check if it is within the user specified gap value and add to group
|
||||||
|
local curr_image = image
|
||||||
for i, image in ipairs(images) do
|
if GetTimeDiff(curr_image, prev_image) <= GUI.gap.value then
|
||||||
if i == 1 then
|
images[i]:group_with(images[i-1])
|
||||||
prev_image = image
|
end
|
||||||
image:make_group_leader()
|
prev_image = curr_image
|
||||||
elseif string.match(image.exif_datetime_taken, '[%d]') ~= nil then --make sure current image has a timestamp, if so check if it is within the user specified gap value and add to group
|
end
|
||||||
local curr_image = image
|
end
|
||||||
if GetTimeDiff(curr_image, prev_image) <= GUI.gap.value then
|
end
|
||||||
images[i]:group_with(images[i-1].group_leader)
|
|
||||||
end
|
local function install_module()
|
||||||
prev_image = curr_image
|
if not Ag.module_installed then
|
||||||
end
|
dt.print_log("installing module")
|
||||||
end
|
dt.register_lib(
|
||||||
end
|
MOD, -- Module name
|
||||||
|
_('auto group'), -- name
|
||||||
local function install_module()
|
true, -- expandable
|
||||||
if not Ag.module_installed then
|
true, -- resetable
|
||||||
dt.print_log("installing module")
|
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 99}}, -- containers
|
||||||
dt.register_lib(
|
dt.new_widget("box"){
|
||||||
MOD, -- Module name
|
orientation = "vertical",
|
||||||
_('auto group'), -- name
|
GUI.gap,
|
||||||
true, -- expandable
|
GUI.selected,
|
||||||
true, -- resetable
|
GUI.collection
|
||||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 99}}, -- containers
|
}
|
||||||
dt.new_widget("box"){
|
)
|
||||||
orientation = "vertical",
|
Ag.module_installed = true
|
||||||
GUI.gap,
|
dt.print_log("module installed")
|
||||||
GUI.selected,
|
dt.print_log("styles module visibility is " .. tostring(dt.gui.libs["styles"].visible))
|
||||||
GUI.collection
|
end
|
||||||
}
|
end
|
||||||
)
|
|
||||||
Ag.module_installed = true
|
local function destroy()
|
||||||
dt.print_log("module installed")
|
dt.gui.libs[MOD].visible = false
|
||||||
dt.print_log("styles module visibility is " .. tostring(dt.gui.libs["styles"].visible))
|
end
|
||||||
end
|
|
||||||
end
|
local function restart()
|
||||||
|
dt.gui.libs[MOD].visible = true
|
||||||
local function destroy()
|
end
|
||||||
dt.gui.libs[MOD].visible = false
|
|
||||||
end
|
-- GUI --
|
||||||
|
temp = dt.preferences.read(MOD, 'active_gap', 'integer')
|
||||||
local function restart()
|
if not InRange(temp, 1, 86400) then temp = 3 end
|
||||||
dt.gui.libs[MOD].visible = true
|
GUI.gap = dt.new_widget('slider'){
|
||||||
end
|
label = _('group gap [sec.]'),
|
||||||
|
tooltip = _('minimum gap, in seconds, between groups'),
|
||||||
-- GUI --
|
soft_min = 1,
|
||||||
temp = dt.preferences.read(MOD, 'active_gap', 'integer')
|
soft_max = 60,
|
||||||
if not InRange(temp, 1, 86400) then temp = 3 end
|
hard_min = 1,
|
||||||
GUI.gap = dt.new_widget('slider'){
|
hard_max = 86400,
|
||||||
label = _('group gap [sec.]'),
|
step = 1,
|
||||||
tooltip = _('minimum gap, in seconds, between groups'),
|
digits = 0,
|
||||||
soft_min = 1,
|
value = temp,
|
||||||
soft_max = 60,
|
reset_callback = function(self)
|
||||||
hard_min = 1,
|
self.value = 3
|
||||||
hard_max = 86400,
|
end
|
||||||
step = 1,
|
}
|
||||||
digits = 0,
|
GUI.selected = dt.new_widget("button"){
|
||||||
value = temp,
|
label = _('auto group: selected'),
|
||||||
reset_callback = function(self)
|
tooltip =_('auto group selected images'),
|
||||||
self.value = 3
|
clicked_callback = function() main(false) end
|
||||||
end
|
}
|
||||||
}
|
GUI.collection = dt.new_widget("button"){
|
||||||
GUI.selected = dt.new_widget("button"){
|
label = _('auto group: collection'),
|
||||||
label = _('auto group: selected'),
|
tooltip =_('auto group the entire collection'),
|
||||||
tooltip =_('auto group selected images'),
|
clicked_callback = function() main(true) end
|
||||||
clicked_callback = function() main(false) end
|
}
|
||||||
}
|
|
||||||
GUI.collection = dt.new_widget("button"){
|
if dt.gui.current_view().id == "lighttable" then
|
||||||
label = _('auto group: collection'),
|
install_module()
|
||||||
tooltip =_('auto group the entire collection'),
|
else
|
||||||
clicked_callback = function() main(true) end
|
if not Ag.event_registered then
|
||||||
}
|
dt.register_event(
|
||||||
|
"AutoGrouper", "view-changed",
|
||||||
if dt.gui.current_view().id == "lighttable" then
|
function(event, old_view, new_view)
|
||||||
install_module()
|
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||||
else
|
install_module()
|
||||||
if not Ag.event_registered then
|
end
|
||||||
dt.register_event(
|
end
|
||||||
"AutoGrouper", "view-changed",
|
)
|
||||||
function(event, old_view, new_view)
|
Ag.event_registered = true
|
||||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
end
|
||||||
install_module()
|
end
|
||||||
end
|
|
||||||
end
|
script_data.destroy = destroy
|
||||||
)
|
script_data.destroy_method = "hide"
|
||||||
Ag.event_registered = true
|
script_data.restart = restart
|
||||||
end
|
script_data.show = restart
|
||||||
end
|
|
||||||
|
return script_data
|
||||||
script_data.destroy = destroy
|
|
||||||
script_data.destroy_method = "hide"
|
|
||||||
script_data.restart = restart
|
|
||||||
script_data.show = restart
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
|
|||||||
@@ -1,258 +1,254 @@
|
|||||||
--[[Collect Helper plugin for darktable
|
--[[Collect Helper plugin for darktable
|
||||||
|
|
||||||
copyright (c) 2019 Kevin Ertel
|
copyright (c) 2019 Kevin Ertel
|
||||||
|
|
||||||
darktable is free software: you can redistribute it and/or modify
|
darktable is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
darktable is distributed in the hope that it will be useful,
|
darktable is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
--[[About this plugin
|
--[[About this plugin
|
||||||
This plugin adds the button(s) to the "Selected Images" module:
|
This plugin adds the button(s) to the "Selected Images" module:
|
||||||
1) Return to Previous Collection
|
1) Return to Previous Collection
|
||||||
2) Collect on image's Folder
|
2) Collect on image's Folder
|
||||||
3) Collect on image's Color Label(s)
|
3) Collect on image's Color Label(s)
|
||||||
4) Collect on All (AND)
|
4) Collect on All (AND)
|
||||||
|
|
||||||
It also adds 3 preferences to the lua options dialog box which allow the user to activate/deactivate the 3 "Collect on" buttons.
|
It also adds 3 preferences to the lua options dialog box which allow the user to activate/deactivate the 3 "Collect on" buttons.
|
||||||
|
|
||||||
Button Behavior:
|
Button Behavior:
|
||||||
1) Return to Previous Collection - Will reset the collect parameters to the previously active settings
|
1) Return to Previous Collection - Will reset the collect parameters to the previously active settings
|
||||||
2) Collect on image's Folder - Will change the collect parameters to be "Folder" with a value of the selected image's folder location
|
2) Collect on image's Folder - Will change the collect parameters to be "Folder" with a value of the selected image's folder location
|
||||||
3) Collect on image's Color Label(s) - Will change the collect parameter to be "Color" with a value of the selected images color labels, will apply multiple parameters with AND logic if multiple exist
|
3) Collect on image's Color Label(s) - Will change the collect parameter to be "Color" with a value of the selected images color labels, will apply multiple parameters with AND logic if multiple exist
|
||||||
4) Collect on All (AND) - Will collect on all parameters activated by the preferences dialog, as such this button is redundant if you only have one of the two other options enabled
|
4) Collect on All (AND) - Will collect on all parameters activated by the preferences dialog, as such this button is redundant if you only have one of the two other options enabled
|
||||||
|
|
||||||
----REQUIRED SOFTWARE----
|
----REQUIRED SOFTWARE----
|
||||||
NA
|
NA
|
||||||
|
|
||||||
----USAGE----
|
----USAGE----
|
||||||
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts )
|
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts )
|
||||||
1) Copy this file in to your "lua/contrib" folder where all other scripts reside.
|
1) Copy this file 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
|
2) Require this file in your luarc file, as with any other dt plug-in
|
||||||
|
|
||||||
Select the photo you wish to change you collection based on.
|
Select the photo you wish to change you collection based on.
|
||||||
In the "Selected Images" module click on "Collect on this Image"
|
In the "Selected Images" module click on "Collect on this Image"
|
||||||
|
|
||||||
----KNOWN ISSUES----
|
----KNOWN ISSUES----
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
local previous = nil
|
local previous = nil
|
||||||
local all_active = false
|
local all_active = false
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "CollectHelper")
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("CollectHelper",dt.configuration.config_dir.."/lua/locale/")
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
du.check_min_api_version("7.0.0", "CollectHelper")
|
||||||
end
|
|
||||||
|
-- return data structure for script_manager
|
||||||
-- return data structure for script_manager
|
|
||||||
|
local script_data = {}
|
||||||
local script_data = {}
|
|
||||||
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.metadata = {
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
name = _("collection helper"),
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
purpose = _("add collection helper buttons"),
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
author = "Kevin Ertel",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/CollectHelper/"
|
local function _(msgid)
|
||||||
}
|
return gettext.dgettext("CollectHelper", msgid)
|
||||||
|
end
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
-- FUNCTION --
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
local function CheckSingleImage(selection)
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
if #selection ~= 1 then
|
||||||
|
dt.print(_("Please select a single image"))
|
||||||
-- FUNCTION --
|
return true
|
||||||
local function CheckSingleImage(selection)
|
end
|
||||||
if #selection ~= 1 then
|
return false
|
||||||
dt.print(_("please select a single image"))
|
end
|
||||||
return true
|
|
||||||
end
|
local function CheckHasColorLabel(selection)
|
||||||
return false
|
local ret = false
|
||||||
end
|
for _,image in pairs(selection) do
|
||||||
|
if image.red then ret = true end
|
||||||
local function CheckHasColorLabel(selection)
|
if image.blue then ret = true end
|
||||||
local ret = false
|
if image.green then ret = true end
|
||||||
for _,image in pairs(selection) do
|
if image.yellow then ret = true end
|
||||||
if image.red then ret = true end
|
if image.purple then ret = true end
|
||||||
if image.blue then ret = true end
|
end
|
||||||
if image.green then ret = true end
|
return ret
|
||||||
if image.yellow then ret = true end
|
end
|
||||||
if image.purple then ret = true end
|
local function PreviousCollection()
|
||||||
end
|
if previous ~= nil then
|
||||||
return ret
|
previous = dt.gui.libs.collect.filter(previous)
|
||||||
end
|
end
|
||||||
local function PreviousCollection()
|
end
|
||||||
if previous ~= nil then
|
|
||||||
previous = dt.gui.libs.collect.filter(previous)
|
local function CollectOnFolder(all_rules, all_active)
|
||||||
end
|
local images = dt.gui.selection()
|
||||||
end
|
if CheckSingleImage(images) then
|
||||||
|
return
|
||||||
local function CollectOnFolder(all_rules, all_active)
|
end
|
||||||
local images = dt.gui.selection()
|
local rules = {}
|
||||||
if CheckSingleImage(images) then
|
local rule = dt.gui.libs.collect.new_rule()
|
||||||
return
|
rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
||||||
end
|
rule.data = images[1].path
|
||||||
local rules = {}
|
rule.item = "DT_COLLECTION_PROP_FOLDERS"
|
||||||
local rule = dt.gui.libs.collect.new_rule()
|
table.insert(rules, rule)
|
||||||
rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
if all_active then
|
||||||
rule.data = images[1].path
|
for _,active_rule in pairs(rules) do
|
||||||
rule.item = "DT_COLLECTION_PROP_FOLDERS"
|
table.insert(all_rules, active_rule)
|
||||||
table.insert(rules, rule)
|
end
|
||||||
if all_active then
|
return all_rules
|
||||||
for _,active_rule in pairs(rules) do
|
else
|
||||||
table.insert(all_rules, active_rule)
|
previous = dt.gui.libs.collect.filter(rules)
|
||||||
end
|
end
|
||||||
return all_rules
|
end
|
||||||
else
|
|
||||||
previous = dt.gui.libs.collect.filter(rules)
|
local function CollectOnColors(all_rules, all_active)
|
||||||
end
|
local images = dt.gui.selection()
|
||||||
end
|
if CheckSingleImage(images) then
|
||||||
|
return
|
||||||
local function CollectOnColors(all_rules, all_active)
|
end
|
||||||
local images = dt.gui.selection()
|
if not CheckHasColorLabel(images) then
|
||||||
if CheckSingleImage(images) then
|
dt.print(_('select an image with an active color label'))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not CheckHasColorLabel(images) then
|
for _,image in pairs(images) do
|
||||||
dt.print(_('select an image with an active color label'))
|
local rules = {}
|
||||||
return
|
if image.red then
|
||||||
end
|
local red_rule = dt.gui.libs.collect.new_rule()
|
||||||
for _,image in pairs(images) do
|
red_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
||||||
local rules = {}
|
red_rule.data = "red"
|
||||||
if image.red then
|
red_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
||||||
local red_rule = dt.gui.libs.collect.new_rule()
|
table.insert(rules, red_rule)
|
||||||
red_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
end
|
||||||
red_rule.data = "red"
|
if image.blue then
|
||||||
red_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
local blue_rule = dt.gui.libs.collect.new_rule()
|
||||||
table.insert(rules, red_rule)
|
blue_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
||||||
end
|
blue_rule.data = "blue"
|
||||||
if image.blue then
|
blue_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
||||||
local blue_rule = dt.gui.libs.collect.new_rule()
|
table.insert(rules, blue_rule)
|
||||||
blue_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
end
|
||||||
blue_rule.data = "blue"
|
if image.green then
|
||||||
blue_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
local green_rule = dt.gui.libs.collect.new_rule()
|
||||||
table.insert(rules, blue_rule)
|
green_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
||||||
end
|
green_rule.data = "green"
|
||||||
if image.green then
|
green_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
||||||
local green_rule = dt.gui.libs.collect.new_rule()
|
table.insert(rules, green_rule)
|
||||||
green_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
end
|
||||||
green_rule.data = "green"
|
if image.yellow then
|
||||||
green_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
local yellow_rule = dt.gui.libs.collect.new_rule()
|
||||||
table.insert(rules, green_rule)
|
yellow_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
||||||
end
|
yellow_rule.data = "yellow"
|
||||||
if image.yellow then
|
yellow_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
||||||
local yellow_rule = dt.gui.libs.collect.new_rule()
|
table.insert(rules, yellow_rule)
|
||||||
yellow_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
end
|
||||||
yellow_rule.data = "yellow"
|
if image.purple then
|
||||||
yellow_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
local purple_rule = dt.gui.libs.collect.new_rule()
|
||||||
table.insert(rules, yellow_rule)
|
purple_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
||||||
end
|
purple_rule.data = "purple"
|
||||||
if image.purple then
|
purple_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
||||||
local purple_rule = dt.gui.libs.collect.new_rule()
|
table.insert(rules, purple_rule)
|
||||||
purple_rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
end
|
||||||
purple_rule.data = "purple"
|
if all_active then
|
||||||
purple_rule.item = "DT_COLLECTION_PROP_COLORLABEL"
|
for _,active_rule in pairs(rules) do
|
||||||
table.insert(rules, purple_rule)
|
table.insert(all_rules, active_rule)
|
||||||
end
|
end
|
||||||
if all_active then
|
return all_rules
|
||||||
for _,active_rule in pairs(rules) do
|
else
|
||||||
table.insert(all_rules, active_rule)
|
previous = dt.gui.libs.collect.filter(rules)
|
||||||
end
|
end
|
||||||
return all_rules
|
end
|
||||||
else
|
end
|
||||||
previous = dt.gui.libs.collect.filter(rules)
|
|
||||||
end
|
local function CollectOnAll_AND()
|
||||||
end
|
local images = dt.gui.selection()
|
||||||
end
|
if CheckSingleImage(images) then
|
||||||
|
return
|
||||||
local function CollectOnAll_AND()
|
end
|
||||||
local images = dt.gui.selection()
|
local rules = {}
|
||||||
if CheckSingleImage(images) then
|
if dt.preferences.read('module_CollectHelper','folder','bool') then
|
||||||
return
|
rules = CollectOnFolder(rules, true)
|
||||||
end
|
end
|
||||||
local rules = {}
|
if dt.preferences.read('module_CollectHelper','colors','bool') then
|
||||||
if dt.preferences.read('module_CollectHelper','folder','bool') then
|
rules = CollectOnColors(rules, true)
|
||||||
rules = CollectOnFolder(rules, true)
|
end
|
||||||
end
|
previous = dt.gui.libs.collect.filter(rules)
|
||||||
if dt.preferences.read('module_CollectHelper','colors','bool') then
|
end
|
||||||
rules = CollectOnColors(rules, true)
|
|
||||||
end
|
local function destroy()
|
||||||
previous = dt.gui.libs.collect.filter(rules)
|
dt.gui.libs.image.destroy_action("CollectHelper_prev")
|
||||||
end
|
if dt.preferences.read('module_CollectHelper','folder','bool') then
|
||||||
|
dt.gui.libs.image.destroy_action("CollectHelper_folder")
|
||||||
local function destroy()
|
end
|
||||||
dt.gui.libs.image.destroy_action("CollectHelper_prev")
|
if dt.preferences.read('module_CollectHelper','colors','bool') then
|
||||||
if dt.preferences.read('module_CollectHelper','folder','bool') then
|
dt.gui.libs.image.destroy_action("CollectHelper_labels")
|
||||||
dt.gui.libs.image.destroy_action("CollectHelper_folder")
|
end
|
||||||
end
|
if dt.preferences.read('module_CollectHelper','all_and','bool') then
|
||||||
if dt.preferences.read('module_CollectHelper','colors','bool') then
|
dt.gui.libs.image.destroy_action("CollectHelper_and")
|
||||||
dt.gui.libs.image.destroy_action("CollectHelper_labels")
|
end
|
||||||
end
|
end
|
||||||
if dt.preferences.read('module_CollectHelper','all_and','bool') then
|
|
||||||
dt.gui.libs.image.destroy_action("CollectHelper_and")
|
-- GUI --
|
||||||
end
|
dt.gui.libs.image.register_action(
|
||||||
end
|
"CollectHelper_prev", _("collect: previous"),
|
||||||
|
function() PreviousCollection() end,
|
||||||
-- GUI --
|
_("Sets the Collect parameters to be the previously active parameters")
|
||||||
dt.gui.libs.image.register_action(
|
)
|
||||||
"CollectHelper_prev", _("collect: previous"),
|
if dt.preferences.read('module_CollectHelper','folder','bool') then
|
||||||
function() PreviousCollection() end,
|
dt.gui.libs.image.register_action(
|
||||||
_("sets the collect parameters to be the previously active parameters")
|
"CollectHelper_folder", _("collect: folder"),
|
||||||
)
|
function() CollectOnFolder(_ , false) end,
|
||||||
if dt.preferences.read('module_CollectHelper','folder','bool') then
|
_("Sets the Collect parameters to be the selected images's folder")
|
||||||
dt.gui.libs.image.register_action(
|
)
|
||||||
"CollectHelper_folder", _("collect: folder"),
|
end
|
||||||
function() CollectOnFolder(_ , false) end,
|
if dt.preferences.read('module_CollectHelper','colors','bool') then
|
||||||
_("sets the collect parameters to be the selected images's folder")
|
dt.gui.libs.image.register_action(
|
||||||
)
|
"CollectHelper_labels", _("collect: color label(s)"),
|
||||||
end
|
function() CollectOnColors(_ , false) end,
|
||||||
if dt.preferences.read('module_CollectHelper','colors','bool') then
|
_("Sets the Collect parameters to be the selected images's color label(s)")
|
||||||
dt.gui.libs.image.register_action(
|
)
|
||||||
"CollectHelper_labels", _("collect: color label(s)"),
|
end
|
||||||
function() CollectOnColors(_ , false) end,
|
if dt.preferences.read('module_CollectHelper','all_and','bool') then
|
||||||
_("sets the collect parameters to be the selected images's color label(s)")
|
dt.gui.libs.image.register_action(
|
||||||
)
|
"CollectHelper_and", _("collect: all (AND)"),
|
||||||
end
|
function() CollectOnAll_AND() end,
|
||||||
if dt.preferences.read('module_CollectHelper','all_and','bool') then
|
_("Sets the Collect parameters based on all activated CollectHelper options")
|
||||||
dt.gui.libs.image.register_action(
|
)
|
||||||
"CollectHelper_and", _("collect: all (AND)"),
|
end
|
||||||
function() CollectOnAll_AND() end,
|
|
||||||
_("sets the collect parameters based on all activated CollectHelper options")
|
-- PREFERENCES --
|
||||||
)
|
dt.preferences.register("module_CollectHelper", "all_and", -- name
|
||||||
end
|
"bool", -- type
|
||||||
|
_('CollectHelper: All'), -- label
|
||||||
-- PREFERENCES --
|
_('Will create a collect parameter set that utilizes all enabled CollectHelper types (AND)'), -- tooltip
|
||||||
dt.preferences.register("module_CollectHelper", "all_and", -- name
|
true -- default
|
||||||
"bool", -- type
|
)
|
||||||
_('CollectHelper: all'), -- label
|
dt.preferences.register("module_CollectHelper", "colors", -- name
|
||||||
_('creates a collect parameter set that utilizes all enabled CollectHelper types (and)'), -- tooltip
|
"bool", -- type
|
||||||
true -- default
|
_('CollectHelper: Color Label(s)'), -- label
|
||||||
)
|
_('Enable the button that allows you to swap to a collection based on selected image\'s COLOR LABEL(S)'), -- tooltip
|
||||||
dt.preferences.register("module_CollectHelper", "colors", -- name
|
true -- default
|
||||||
"bool", -- type
|
)
|
||||||
_('CollectHelper: color label(s)'), -- label
|
dt.preferences.register("module_CollectHelper", "folder", -- name
|
||||||
_('enable the button that allows you to swap to a collection based on selected image\'s color label(s)'), -- tooltip
|
"bool", -- type
|
||||||
true -- default
|
_('CollectHelper: Folder'), -- label
|
||||||
)
|
_('Enable the button that allows you to swap to a collection based on selected image\'s FOLDER location'), -- tooltip
|
||||||
dt.preferences.register("module_CollectHelper", "folder", -- name
|
true -- default
|
||||||
"bool", -- type
|
)
|
||||||
_('CollectHelper: folder'), -- label
|
|
||||||
_('enable the button that allows you to swap to a collection based on selected image\'s folder location'), -- tooltip
|
script_data.destroy = destroy
|
||||||
true -- default
|
|
||||||
)
|
return script_data
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
|
|||||||
@@ -1,486 +1,479 @@
|
|||||||
--[[HDRMerge plugin for darktable
|
--[[HDRMerge plugin for darktable
|
||||||
|
|
||||||
copyright (c) 2018 Kevin Ertel
|
copyright (c) 2018 Kevin Ertel
|
||||||
|
|
||||||
darktable is free software: you can redistribute it and/or modify
|
darktable is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
darktable is distributed in the hope that it will be useful,
|
darktable is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
--[[About this Plugin
|
--[[About this Plugin
|
||||||
This plugin adds the module 'HDRMerge' to darktable's lighttable view
|
This plugin adds the module 'HDRMerge' to darktable's lighttable view
|
||||||
|
|
||||||
----REQUIRED SOFTWARE----
|
----REQUIRED SOFTWARE----
|
||||||
HDRMerge ver. 4.5 or greater
|
HDRMerge ver. 4.5 or greater
|
||||||
|
|
||||||
----USAGE----
|
----USAGE----
|
||||||
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts )
|
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts )
|
||||||
1) Copy this file in to your 'lua/contrib' folder where all other scripts reside.
|
1) Copy this file 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
|
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
|
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.
|
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.
|
Additional tags or style can be applied on auto import as well, if you desire.
|
||||||
|
|
||||||
Base Options:
|
Base Options:
|
||||||
Select your desired BPS (bits per sample and Embedded Preview Size.
|
Select your desired BPS (bits per sample and Embedded Preview Size.
|
||||||
|
|
||||||
Batch Options:
|
Batch Options:
|
||||||
Select if you want to run in batch mode or not
|
Select if you want to run in batch mode or not
|
||||||
Select the gap, in seconds, between images for auto grouping in batch mode
|
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
|
See HDRMerge manual for further detail: http://jcelaya.github.io/hdrmerge/documentation/2014/07/11/user-manual.html
|
||||||
|
|
||||||
Auto-import Options:
|
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
|
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 dt = require 'darktable'
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require 'lib/dtutils.file'
|
local df = require 'lib/dtutils.file'
|
||||||
local dsys = require 'lib/dtutils.system'
|
local dsys = require 'lib/dtutils.system'
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "HDRmerge")
|
du.check_min_api_version("7.0.0", "HDRmerge")
|
||||||
|
|
||||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
-- return data structure for script_manager
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
local script_data = {}
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
script_data.destroy = nil -- function to destory the script
|
||||||
end
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
-- return data structure for script_manager
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
local mod = 'module_HDRMerge'
|
||||||
script_data.metadata = {
|
local os_path_seperator = '/'
|
||||||
name = _("HDR merge"),
|
if dt.configuration.running_os == 'windows' then os_path_seperator = '\\' end
|
||||||
purpose = _("merge bracketed images into an HDR DNG image"),
|
local CURR_API_STRING = dt.configuration.api_version_string
|
||||||
author = "Kevin Ertel",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/HDRmerge"
|
-- 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/')
|
||||||
script_data.destroy = nil -- function to destory the script
|
local function _(msgid)
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
return gettext.dgettext('HDRMerge', msgid)
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
end
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
local temp
|
||||||
|
local HDRM = { --HDRMerge Program Table
|
||||||
local mod = 'module_HDRMerge'
|
name = 'HDRMerge',
|
||||||
local os_path_seperator = '/'
|
bin = '',
|
||||||
if dt.configuration.running_os == 'windows' then os_path_seperator = '\\' end
|
first_run = true,
|
||||||
local CURR_API_STRING = dt.configuration.api_version_string
|
install_error = false,
|
||||||
|
arg_string = '',
|
||||||
local temp
|
images_string = '',
|
||||||
local HDRM = { --HDRMerge Program Table
|
args = {
|
||||||
name = 'HDRMerge',
|
bps = {text = '-b ', style = 'integer'},
|
||||||
bin = '',
|
size = {text = '-p ', style = 'string'},
|
||||||
first_run = true,
|
batch = {text = '-B ', style = 'bool'},
|
||||||
install_error = false,
|
gap = {text = '-g ', style = 'integer'}
|
||||||
arg_string = '',
|
}
|
||||||
images_string = '',
|
}
|
||||||
args = {
|
local GUI = { --GUI Elements Table
|
||||||
bps = {text = '-b ', style = 'integer'},
|
HDR = {
|
||||||
size = {text = '-p ', style = 'string'},
|
bps ={},
|
||||||
batch = {text = '-B ', style = 'bool'},
|
size ={},
|
||||||
gap = {text = '-g ', style = 'integer'}
|
batch ={},
|
||||||
}
|
gap ={}
|
||||||
}
|
},
|
||||||
local GUI = { --GUI Elements Table
|
Target = {
|
||||||
HDR = {
|
style ={},
|
||||||
bps ={},
|
copy_tags ={},
|
||||||
size ={},
|
add_tags ={}
|
||||||
batch ={},
|
},
|
||||||
gap ={}
|
run = {},
|
||||||
},
|
stack = {},
|
||||||
Target = {
|
options = {},
|
||||||
style ={},
|
exes = {
|
||||||
copy_tags ={},
|
HDRMerge = {},
|
||||||
add_tags ={}
|
update = {},
|
||||||
},
|
}
|
||||||
run = {},
|
}
|
||||||
stack = {},
|
|
||||||
options = {},
|
HDRM.module_installed = false
|
||||||
exes = {
|
HDRM.event_registered = false
|
||||||
HDRMerge = {},
|
|
||||||
update = {},
|
|
||||||
}
|
--Detect User Styles--
|
||||||
}
|
local styles = dt.styles
|
||||||
|
local styles_count = 1 -- 'none' = 1
|
||||||
HDRM.module_installed = false
|
for _,i in pairs(dt.styles) do
|
||||||
HDRM.event_registered = false
|
if type(i) == 'userdata' then styles_count = styles_count + 1 end
|
||||||
|
end
|
||||||
|
|
||||||
--Detect User Styles--
|
local function InRange(test, low, high) --tests if test value is within range of low and high (inclusive)
|
||||||
local styles = dt.styles
|
if test >= low and test <= high then
|
||||||
local styles_count = 1 -- 'none' = 1
|
return true
|
||||||
for _,i in pairs(dt.styles) do
|
else
|
||||||
if type(i) == 'userdata' then styles_count = styles_count + 1 end
|
return false
|
||||||
end
|
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
|
local function GetFileName(full_path) --Parses a full path (path/filename_identifier.extension) into individual parts
|
||||||
return true
|
--[[Input: Folder1/Folder2/Folder3/Img_0001.CR2
|
||||||
else
|
|
||||||
return false
|
Returns:
|
||||||
end
|
path: Folder1/Folder2/Folder3/
|
||||||
end
|
filename: Img_0001
|
||||||
|
identifier: 0001
|
||||||
local function GetFileName(full_path) --Parses a full path (path/filename_identifier.extension) into individual parts
|
extension: .CR2
|
||||||
--[[Input: Folder1/Folder2/Folder3/Img_0001.CR2
|
|
||||||
|
EX:
|
||||||
Returns:
|
path_1, file_1, id_1, ext_1 = GetFileName(full_path_1)
|
||||||
path: Folder1/Folder2/Folder3/
|
]]
|
||||||
filename: Img_0001
|
local path = string.match(full_path, '.*[\\/]')
|
||||||
identifier: 0001
|
local filename = string.gsub(string.match(full_path, '[%w-_]*%.') , '%.' , '' )
|
||||||
extension: .CR2
|
local identifier = string.match(filename, '%d*$')
|
||||||
|
local extension = string.match(full_path, '%.%w*')
|
||||||
EX:
|
return path, filename, identifier, extension
|
||||||
path_1, file_1, id_1, ext_1 = GetFileName(full_path_1)
|
end
|
||||||
]]
|
|
||||||
local path = string.match(full_path, '.*[\\/]')
|
local function CleanSpaces(text) --removes spaces from the front and back of passed in text
|
||||||
local filename = string.gsub(string.match(full_path, '[%w-_]*%.') , '%.' , '' )
|
text = string.gsub(text,'^%s*','')
|
||||||
local identifier = string.match(filename, '%d*$')
|
text = string.gsub(text,'%s*$','')
|
||||||
local extension = string.match(full_path, '%.%w*')
|
return text
|
||||||
return path, filename, identifier, extension
|
end
|
||||||
end
|
|
||||||
|
local function BuildExecuteCmd(prog_table) --creates a program command using elements of the passed in program table
|
||||||
local function CleanSpaces(text) --removes spaces from the front and back of passed in text
|
local result = CleanSpaces(prog_table.bin)..' '..CleanSpaces(prog_table.arg_string)..' '..CleanSpaces(prog_table.images_string)
|
||||||
text = string.gsub(text,'^%s*','')
|
return result
|
||||||
text = string.gsub(text,'%s*$','')
|
end
|
||||||
return text
|
|
||||||
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
|
||||||
local function BuildExecuteCmd(prog_table) --creates a program command using elements of the passed in program table
|
if prog.first_run then
|
||||||
local result = CleanSpaces(prog_table.bin)..' '..CleanSpaces(prog_table.arg_string)..' '..CleanSpaces(prog_table.images_string)
|
prog.bin = df.check_if_bin_exists(prog.name)
|
||||||
return result
|
if not prog.bin then
|
||||||
end
|
prog.install_error = true
|
||||||
|
dt.preferences.write(mod, 'bin_exists', 'bool', false)
|
||||||
local function PreCall(prog_tbl) --looks to see if this is the first call, if so checks to see if program is installed properly
|
else
|
||||||
for _,prog in pairs(prog_tbl) do
|
prog.bin = CleanSpaces(prog.bin)
|
||||||
if prog.first_run then
|
end
|
||||||
prog.bin = df.check_if_bin_exists(prog.name)
|
prog.first_run = false
|
||||||
if not prog.bin then
|
end
|
||||||
prog.install_error = true
|
end
|
||||||
dt.preferences.write(mod, 'bin_exists', 'bool', false)
|
if not dt.preferences.read(mod, 'bin_exists', 'bool') then
|
||||||
else
|
GUI.stack.active = 2
|
||||||
prog.bin = CleanSpaces(prog.bin)
|
dt.print(_('please update you binary location'))
|
||||||
end
|
end
|
||||||
prog.first_run = false
|
end
|
||||||
end
|
|
||||||
end
|
local function ExeUpdate(prog_tbl)
|
||||||
if not dt.preferences.read(mod, 'bin_exists', 'bool') then
|
dt.preferences.write(mod, 'bin_exists', 'bool', true)
|
||||||
GUI.stack.active = 2
|
for _,prog in pairs(prog_tbl) do
|
||||||
dt.print(_('please update you binary location'))
|
dt.preferences.write('executable_paths', prog.name, 'string', GUI.exes[prog.name].value)
|
||||||
end
|
prog.bin = df.check_if_bin_exists(prog.name)
|
||||||
end
|
if not prog.bin then
|
||||||
|
prog.install_error = true
|
||||||
local function ExeUpdate(prog_tbl)
|
dt.preferences.write(mod, 'bin_exists', 'bool', false)
|
||||||
dt.preferences.write(mod, 'bin_exists', 'bool', true)
|
else
|
||||||
for _,prog in pairs(prog_tbl) do
|
prog.bin = CleanSpaces(prog.bin)
|
||||||
dt.preferences.write('executable_paths', prog.name, 'string', GUI.exes[prog.name].value)
|
end
|
||||||
prog.bin = df.check_if_bin_exists(prog.name)
|
prog.first_run = false
|
||||||
if not prog.bin then
|
end
|
||||||
prog.install_error = true
|
if dt.preferences.read(mod, 'bin_exists', 'bool') then
|
||||||
dt.preferences.write(mod, 'bin_exists', 'bool', false)
|
GUI.stack.active = 1
|
||||||
else
|
dt.print(_('update successful'))
|
||||||
prog.bin = CleanSpaces(prog.bin)
|
else
|
||||||
end
|
dt.print(_('update unsuccessful, please try again'))
|
||||||
prog.first_run = false
|
end
|
||||||
end
|
end
|
||||||
if dt.preferences.read(mod, 'bin_exists', 'bool') then
|
|
||||||
GUI.stack.active = 1
|
local function UpdateActivePreference() --sliders & entry boxes do not have a click/changed callback, so their values must be saved to the active preference
|
||||||
dt.print(_('update successful'))
|
temp = GUI.HDR.gap.value
|
||||||
else
|
dt.preferences.write(mod, 'active_gap', 'integer', temp)
|
||||||
dt.print(_('update unsuccessful, please try again'))
|
temp = GUI.Target.add_tags.text
|
||||||
end
|
dt.preferences.write(mod, 'active_add_tags', 'string', temp)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function UpdateActivePreference() --sliders & entry boxes do not have a click/changed callback, so their values must be saved to the active preference
|
local function main()
|
||||||
temp = GUI.HDR.gap.value
|
PreCall({HDRM}) --check if furst run then check if install OK
|
||||||
dt.preferences.write(mod, 'active_gap', 'integer', temp)
|
if HDRM.install_error then
|
||||||
temp = GUI.Target.add_tags.text
|
dt.print_error(_('HDRMerge install issue'))
|
||||||
dt.preferences.write(mod, 'active_add_tags', 'string', temp)
|
dt.print(_('HDRMerge install issue, please ensure the binary path is proper'))
|
||||||
end
|
return
|
||||||
|
end
|
||||||
local function main()
|
images = dt.gui.selection() --get selected images
|
||||||
PreCall({HDRM}) --check if furst run then check if install OK
|
if #images < 2 then --ensure enough images selected
|
||||||
if HDRM.install_error then
|
dt.print(_('not enough images selected, select at least 2 images to merge'))
|
||||||
dt.print_error('HDRMerge install issue')
|
return
|
||||||
dt.print(_('HDRMerge install issue, please ensure the binary path is correct'))
|
end
|
||||||
return
|
|
||||||
end
|
UpdateActivePreference() --save current gui elements to active preference so those values will be pre-loaded at next startup
|
||||||
images = dt.gui.selection() --get selected images
|
|
||||||
if #images < 2 then --ensure enough images selected
|
--create image string and output path
|
||||||
dt.print(_('not enough images selected, select at least 2 images to merge'))
|
HDRM.images_string = ''
|
||||||
return
|
local out_path = ''
|
||||||
end
|
local smallest_id = math.huge
|
||||||
|
local smallest_name = ''
|
||||||
UpdateActivePreference() --save current gui elements to active preference so those values will be pre-loaded at next startup
|
local largest_id = 0
|
||||||
|
local source_raw = {}
|
||||||
--create image string and output path
|
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)
|
||||||
HDRM.images_string = ''
|
local curr_image = image.path..os_path_seperator..image.filename
|
||||||
local out_path = ''
|
HDRM.images_string = HDRM.images_string..df.sanitize_filename(curr_image)..' '
|
||||||
local smallest_id = math.huge
|
out_path = image.path
|
||||||
local smallest_name = ''
|
_unused, source_name, source_id = GetFileName(image.filename)
|
||||||
local largest_id = 0
|
source_id = tonumber(source_id) or 0
|
||||||
local source_raw = {}
|
if source_id < smallest_id then
|
||||||
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)
|
smallest_id = source_id
|
||||||
local curr_image = image.path..os_path_seperator..image.filename
|
smallest_name = source_name
|
||||||
HDRM.images_string = HDRM.images_string..df.sanitize_filename(curr_image)..' '
|
source_raw = image
|
||||||
out_path = image.path
|
end
|
||||||
_unused, source_name, source_id = GetFileName(image.filename)
|
if source_id > largest_id then largest_id = source_id end
|
||||||
source_id = tonumber(source_id) or 0
|
end
|
||||||
if source_id < smallest_id then
|
out_path = out_path..os_path_seperator..smallest_name..'-'..largest_id..'.dng'
|
||||||
smallest_id = source_id
|
out_path = df.create_unique_filename(out_path)
|
||||||
smallest_name = source_name
|
|
||||||
source_raw = image
|
--create argument string
|
||||||
end
|
HDRM.arg_string = HDRM.args.bps.text..GUI.HDR.bps.value..' '..HDRM.args.size.text..GUI.HDR.size.value..' '
|
||||||
if source_id > largest_id then largest_id = source_id end
|
if GUI.HDR.batch.value then
|
||||||
end
|
HDRM.arg_string = HDRM.arg_string..HDRM.args.batch.text..HDRM.args.gap.text..math.floor(GUI.HDR.gap.value)..' -a'
|
||||||
out_path = out_path..os_path_seperator..smallest_name..'-'..largest_id..'.dng'
|
else
|
||||||
out_path = df.create_unique_filename(out_path)
|
HDRM.arg_string = HDRM.arg_string..'-o '..df.sanitize_filename(out_path)
|
||||||
|
end
|
||||||
--create argument string
|
|
||||||
HDRM.arg_string = HDRM.args.bps.text..GUI.HDR.bps.value..' '..HDRM.args.size.text..GUI.HDR.size.value..' '
|
-- create run command and execute
|
||||||
if GUI.HDR.batch.value then
|
local run_cmd = BuildExecuteCmd(HDRM)
|
||||||
HDRM.arg_string = HDRM.arg_string..HDRM.args.batch.text..HDRM.args.gap.text..math.floor(GUI.HDR.gap.value)..' -a'
|
resp = dsys.external_command(run_cmd)
|
||||||
else
|
|
||||||
HDRM.arg_string = HDRM.arg_string..'-o '..df.sanitize_filename(out_path)
|
if resp == 0 and not GUI.HDR.batch.value then
|
||||||
end
|
local imported = dt.database.import(out_path) -- import the new file
|
||||||
|
if GUI.Target.style.selected > 1 then -- apply selected style
|
||||||
-- create run command and execute
|
local set_style = styles[GUI.Target.style.selected - 1]
|
||||||
local run_cmd = BuildExecuteCmd(HDRM)
|
dt.styles.apply(set_style , imported)
|
||||||
resp = dsys.external_command(run_cmd)
|
end
|
||||||
|
if GUI.Target.copy_tags.value then -- copy tags from the original file (ignore 'darktable' generated tags)
|
||||||
if resp == 0 and not GUI.HDR.batch.value then
|
local all_tags = dt.tags.get_tags(source_raw)
|
||||||
local imported = dt.database.import(out_path) -- import the new file
|
for _,tag in pairs(all_tags) do
|
||||||
if GUI.Target.style.selected > 1 then -- apply selected style
|
if string.match(tag.name, 'darktable|') == nil then dt.tags.attach(tag, imported) end
|
||||||
local set_style = styles[GUI.Target.style.selected - 1]
|
end
|
||||||
dt.styles.apply(set_style , imported)
|
end
|
||||||
end
|
local set_tag = GUI.Target.add_tags.text
|
||||||
if GUI.Target.copy_tags.value then -- copy tags from the original file (ignore 'darktable' generated tags)
|
if set_tag ~= nil then -- add additional user-specified tags
|
||||||
local all_tags = dt.tags.get_tags(source_raw)
|
for tag in string.gmatch(set_tag, '[^,]+') do
|
||||||
for _,tag in pairs(all_tags) do
|
tag = CleanSpaces(tag)
|
||||||
if string.match(tag.name, 'darktable|') == nil then dt.tags.attach(tag, imported) end
|
tag = dt.tags.create(tag)
|
||||||
end
|
dt.tags.attach(tag, imported)
|
||||||
end
|
end
|
||||||
local set_tag = GUI.Target.add_tags.text
|
end
|
||||||
if set_tag ~= nil then -- add additional user-specified tags
|
dt.print(_('HDRMerge completed successfully'))
|
||||||
for tag in string.gmatch(set_tag, '[^,]+') do
|
else
|
||||||
tag = CleanSpaces(tag)
|
dt.print_error(_('HDRMerge failed'))
|
||||||
tag = dt.tags.create(tag)
|
dt.print(_('HDRMerge failed'))
|
||||||
dt.tags.attach(tag, imported)
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
dt.print(_('HDRMerge completed successfully'))
|
|
||||||
else
|
local function install_module()
|
||||||
dt.print_error('HDRMerge failed')
|
if not HDRM.module_installed then
|
||||||
dt.print(_('HDRMerge failed'))
|
dt.register_lib( -- register HDRMerge module
|
||||||
end
|
'HDRMerge_Lib', -- Module name
|
||||||
|
_('HDRMerge'), -- name
|
||||||
end
|
true, -- expandable
|
||||||
|
true, -- resetable
|
||||||
local function install_module()
|
{[dt.gui.views.lighttable] = {'DT_UI_CONTAINER_PANEL_RIGHT_CENTER', 99}}, -- containers
|
||||||
if not HDRM.module_installed then
|
dt.new_widget('box'){
|
||||||
dt.register_lib( -- register HDRMerge module
|
orientation = 'vertical',
|
||||||
'HDRMerge_Lib', -- Module name
|
GUI.stack
|
||||||
_('HDRMerge'), -- name
|
}
|
||||||
true, -- expandable
|
)
|
||||||
true, -- resetable
|
HDRM.module_installed = true
|
||||||
{[dt.gui.views.lighttable] = {'DT_UI_CONTAINER_PANEL_RIGHT_CENTER', 99}}, -- containers
|
end
|
||||||
dt.new_widget('box'){
|
end
|
||||||
orientation = 'vertical',
|
|
||||||
GUI.stack
|
local function destroy()
|
||||||
}
|
dt.gui.libs["HDRMerge_Lib"].visible = false
|
||||||
)
|
end
|
||||||
HDRM.module_installed = true
|
|
||||||
end
|
local function restart()
|
||||||
end
|
dt.gui.libs["HDRMerge_Lib"].visible = true
|
||||||
|
end
|
||||||
local function destroy()
|
|
||||||
dt.gui.libs["HDRMerge_Lib"].visible = false
|
-- GUI Elements --
|
||||||
end
|
local lbl_hdr = dt.new_widget('section_label'){
|
||||||
|
label = _('HDRMerge options')
|
||||||
local function restart()
|
}
|
||||||
dt.gui.libs["HDRMerge_Lib"].visible = true
|
temp = dt.preferences.read(mod, 'active_bps_ind', 'integer')
|
||||||
end
|
if not InRange(temp, 1, 3) then temp = 3 end
|
||||||
|
GUI.HDR.bps = dt.new_widget('combobox'){
|
||||||
-- GUI Elements --
|
label = _('bits per sample'),
|
||||||
local lbl_hdr = dt.new_widget('section_label'){
|
tooltip =_('number of bits per sample in the output image'),
|
||||||
label = _('HDRMerge options')
|
selected = temp,
|
||||||
}
|
'16','24','32',
|
||||||
temp = dt.preferences.read(mod, 'active_bps_ind', 'integer')
|
changed_callback = function(self)
|
||||||
if not InRange(temp, 1, 3) then temp = 3 end
|
dt.preferences.write(mod, 'active_bps', 'integer', self.value)
|
||||||
GUI.HDR.bps = dt.new_widget('combobox'){
|
dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected)
|
||||||
label = _('bits per sample'),
|
end,
|
||||||
tooltip =_('number of bits per sample in the output image'),
|
reset_callback = function(self)
|
||||||
selected = temp,
|
self.selected = 3
|
||||||
'16','24','32',
|
dt.preferences.write(mod, 'active_bps', 'integer', self.value)
|
||||||
changed_callback = function(self)
|
dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected)
|
||||||
dt.preferences.write(mod, 'active_bps', 'integer', self.value)
|
end
|
||||||
dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected)
|
}
|
||||||
end,
|
temp = dt.preferences.read(mod, 'active_size_ind', 'integer')
|
||||||
reset_callback = function(self)
|
if not InRange(temp, 1, 3) then temp = 2 end
|
||||||
self.selected = 3
|
GUI.HDR.size = dt.new_widget('combobox'){
|
||||||
dt.preferences.write(mod, 'active_bps', 'integer', self.value)
|
label = _('embedded preview size'),
|
||||||
dt.preferences.write(mod, 'active_bps_ind', 'integer', self.selected)
|
tooltip =_('size of the embedded preview in output image'),
|
||||||
end
|
selected = temp,
|
||||||
}
|
_('none'),_('half'),_('full'),
|
||||||
temp = dt.preferences.read(mod, 'active_size_ind', 'integer')
|
changed_callback = function(self)
|
||||||
if not InRange(temp, 1, 3) then temp = 2 end
|
dt.preferences.write(mod, 'active_size', 'string', self.value)
|
||||||
GUI.HDR.size = dt.new_widget('combobox'){
|
dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected)
|
||||||
label = _('embedded preview size'),
|
end,
|
||||||
tooltip =_('size of the embedded preview in output image'),
|
reset_callback = function(self)
|
||||||
selected = temp,
|
self.selected = 2
|
||||||
_('none'),_('half'),_('full'),
|
dt.preferences.write(mod, 'active_size', 'string', self.value)
|
||||||
changed_callback = function(self)
|
dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected)
|
||||||
dt.preferences.write(mod, 'active_size', 'string', self.value)
|
end
|
||||||
dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected)
|
}
|
||||||
end,
|
GUI.HDR.batch = dt.new_widget('check_button'){
|
||||||
reset_callback = function(self)
|
label = _('batch mode'),
|
||||||
self.selected = 2
|
value = dt.preferences.read(mod, 'active_batch', 'bool'),
|
||||||
dt.preferences.write(mod, 'active_size', 'string', self.value)
|
tooltip = _('enable batch mode operation \nNOTE: resultant files will NOT be auto-imported'),
|
||||||
dt.preferences.write(mod, 'active_size_ind', 'integer', self.selected)
|
clicked_callback = function(self)
|
||||||
end
|
dt.preferences.write(mod, 'active_batch', 'bool', self.value)
|
||||||
}
|
GUI.HDR.gap.sensitive = self.value
|
||||||
GUI.HDR.batch = dt.new_widget('check_button'){
|
end,
|
||||||
label = _('batch mode'),
|
reset_callback = function(self) self.value = false end
|
||||||
value = dt.preferences.read(mod, 'active_batch', 'bool'),
|
}
|
||||||
tooltip = _('enable batch mode operation \nNOTE: resultant files will NOT be auto-imported'),
|
temp = dt.preferences.read(mod, 'active_gap', 'integer')
|
||||||
clicked_callback = function(self)
|
if not InRange(temp, 1, 3600) then temp = 3 end
|
||||||
dt.preferences.write(mod, 'active_batch', 'bool', self.value)
|
GUI.HDR.gap = dt.new_widget('slider'){
|
||||||
GUI.HDR.gap.sensitive = self.value
|
label = _('batch gap [sec.]'),
|
||||||
end,
|
tooltip = _('gap, in seconds, between batch mode groups'),
|
||||||
reset_callback = function(self) self.value = false end
|
soft_min = 1,
|
||||||
}
|
soft_max = 30,
|
||||||
temp = dt.preferences.read(mod, 'active_gap', 'integer')
|
hard_min = 1,
|
||||||
if not InRange(temp, 1, 3600) then temp = 3 end
|
hard_max = 3600,
|
||||||
GUI.HDR.gap = dt.new_widget('slider'){
|
step = 1,
|
||||||
label = _('batch gap [sec.]'),
|
digits = 0,
|
||||||
tooltip = _('gap, in seconds, between batch mode groups'),
|
value = temp,
|
||||||
soft_min = 1,
|
sensitive = GUI.HDR.batch.value,
|
||||||
soft_max = 30,
|
reset_callback = function(self)
|
||||||
hard_min = 1,
|
self.value = 3
|
||||||
hard_max = 3600,
|
end
|
||||||
step = 1,
|
}
|
||||||
digits = 0,
|
local lbl_import = dt.new_widget('section_label'){
|
||||||
value = temp,
|
label = _('import options')
|
||||||
sensitive = GUI.HDR.batch.value,
|
}
|
||||||
reset_callback = function(self)
|
GUI.Target.style = dt.new_widget('combobox'){
|
||||||
self.value = 3
|
label = _('apply style on import'),
|
||||||
end
|
tooltip = _('Apply selected style on auto-import to newly created image'),
|
||||||
}
|
selected = 1,
|
||||||
local lbl_import = dt.new_widget('section_label'){
|
_('none'),
|
||||||
label = _('import options')
|
changed_callback = function(self)
|
||||||
}
|
dt.preferences.write(mod, 'active_style', 'string', self.value)
|
||||||
GUI.Target.style = dt.new_widget('combobox'){
|
dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected)
|
||||||
label = _('apply style on import'),
|
end,
|
||||||
tooltip = _('apply selected style on auto-import to newly created image'),
|
reset_callback = function(self)
|
||||||
selected = 1,
|
self.selected = 1
|
||||||
_('none'),
|
dt.preferences.write(mod, 'active_style', 'string', self.value)
|
||||||
changed_callback = function(self)
|
dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected)
|
||||||
dt.preferences.write(mod, 'active_style', 'string', self.value)
|
end
|
||||||
dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected)
|
}
|
||||||
end,
|
for k=1, (styles_count-1) do
|
||||||
reset_callback = function(self)
|
GUI.Target.style[k+1] = styles[k].name
|
||||||
self.selected = 1
|
end
|
||||||
dt.preferences.write(mod, 'active_style', 'string', self.value)
|
temp = dt.preferences.read(mod, 'active_style_ind', 'integer')
|
||||||
dt.preferences.write(mod, 'active_style_ind', 'integer', self.selected)
|
if not InRange(temp, 1, styles_count) then temp = 1 end
|
||||||
end
|
GUI.Target.style.selected = temp
|
||||||
}
|
GUI.Target.copy_tags = dt.new_widget('check_button'){
|
||||||
for k=1, (styles_count-1) do
|
label = _('copy tags'),
|
||||||
GUI.Target.style[k+1] = styles[k].name
|
value = dt.preferences.read(mod, 'active_copy_tags', 'bool'),
|
||||||
end
|
tooltip = _('copy tags from first source image'),
|
||||||
temp = dt.preferences.read(mod, 'active_style_ind', 'integer')
|
clicked_callback = function(self) dt.preferences.write(mod, 'active_copy_tags', 'bool', self.value) end,
|
||||||
if not InRange(temp, 1, styles_count) then temp = 1 end
|
reset_callback = function(self) self.value = true end
|
||||||
GUI.Target.style.selected = temp
|
}
|
||||||
GUI.Target.copy_tags = dt.new_widget('check_button'){
|
temp = dt.preferences.read(mod, 'active_add_tags', 'string')
|
||||||
label = _('copy tags'),
|
if temp == '' then temp = nil end
|
||||||
value = dt.preferences.read(mod, 'active_copy_tags', 'bool'),
|
GUI.Target.add_tags = dt.new_widget('entry'){
|
||||||
tooltip = _('copy tags from first source image'),
|
tooltip = _('Additional tags to be added on import. Seperate with commas, all spaces will be removed'),
|
||||||
clicked_callback = function(self) dt.preferences.write(mod, 'active_copy_tags', 'bool', self.value) end,
|
text = temp,
|
||||||
reset_callback = function(self) self.value = true end
|
placeholder = _('Enter tags, seperated by commas'),
|
||||||
}
|
editable = true
|
||||||
temp = dt.preferences.read(mod, 'active_add_tags', 'string')
|
}
|
||||||
if temp == '' then temp = nil end
|
GUI.run = dt.new_widget('button'){
|
||||||
GUI.Target.add_tags = dt.new_widget('entry'){
|
label = _('merge'),
|
||||||
tooltip = _('additional tags to be added on import, separate with commas, all spaces will be removed'),
|
tooltip =_('run HDRMerge with the above specified settings'),
|
||||||
text = temp,
|
clicked_callback = function() main() end
|
||||||
placeholder = _('enter tags, separated by commas'),
|
}
|
||||||
editable = true
|
GUI.exes.HDRMerge = dt.new_widget('file_chooser_button'){
|
||||||
}
|
title = _('Select HDRmerge executable'),
|
||||||
GUI.run = dt.new_widget('button'){
|
value = df.get_executable_path_preference(HDRM.name),
|
||||||
label = _('merge'),
|
is_directory = false
|
||||||
tooltip =_('run HDRMerge with the above settings'),
|
}
|
||||||
clicked_callback = function() main() end
|
GUI.exes.update = dt.new_widget('button'){
|
||||||
}
|
label = _('update'),
|
||||||
GUI.exes.HDRMerge = dt.new_widget('file_chooser_button'){
|
tooltip =_('update the binary path with current value'),
|
||||||
title = _('select HDRmerge executable'),
|
clicked_callback = function() ExeUpdate({HDRM}) end
|
||||||
value = df.get_executable_path_preference(HDRM.name),
|
}
|
||||||
is_directory = false
|
GUI.options = dt.new_widget('box'){
|
||||||
}
|
orientation = 'vertical',
|
||||||
GUI.exes.update = dt.new_widget('button'){
|
lbl_hdr,
|
||||||
label = _('update'),
|
GUI.HDR.bps,
|
||||||
tooltip =_('update the binary path with current value'),
|
GUI.HDR.size,
|
||||||
clicked_callback = function() ExeUpdate({HDRM}) end
|
GUI.HDR.batch,
|
||||||
}
|
GUI.HDR.gap,
|
||||||
GUI.options = dt.new_widget('box'){
|
lbl_import,
|
||||||
orientation = 'vertical',
|
GUI.Target.style,
|
||||||
lbl_hdr,
|
GUI.Target.copy_tags,
|
||||||
GUI.HDR.bps,
|
GUI.Target.add_tags,
|
||||||
GUI.HDR.size,
|
GUI.run
|
||||||
GUI.HDR.batch,
|
}
|
||||||
GUI.HDR.gap,
|
local exes_box = dt.new_widget('box'){
|
||||||
lbl_import,
|
orientation = 'vertical',
|
||||||
GUI.Target.style,
|
GUI.exes.HDRMerge,
|
||||||
GUI.Target.copy_tags,
|
GUI.exes.update
|
||||||
GUI.Target.add_tags,
|
}
|
||||||
GUI.run
|
GUI.stack = dt.new_widget('stack'){
|
||||||
}
|
GUI.options,
|
||||||
local exes_box = dt.new_widget('box'){
|
exes_box
|
||||||
orientation = 'vertical',
|
}
|
||||||
GUI.exes.HDRMerge,
|
if dt.preferences.read(mod, 'bin_exists', 'bool') then
|
||||||
GUI.exes.update
|
GUI.stack.active = 1
|
||||||
}
|
else
|
||||||
GUI.stack = dt.new_widget('stack'){
|
GUI.stack.active = 2
|
||||||
GUI.options,
|
end
|
||||||
exes_box
|
|
||||||
}
|
if dt.gui.current_view().id == "lighttable" then
|
||||||
if dt.preferences.read(mod, 'bin_exists', 'bool') then
|
install_module()
|
||||||
GUI.stack.active = 1
|
else
|
||||||
else
|
if not HDRM.event_registered then
|
||||||
GUI.stack.active = 2
|
dt.register_event(
|
||||||
end
|
"HDRmerge", "view-changed",
|
||||||
|
function(event, old_view, new_view)
|
||||||
if dt.gui.current_view().id == "lighttable" then
|
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||||
install_module()
|
install_module()
|
||||||
else
|
end
|
||||||
if not HDRM.event_registered then
|
end
|
||||||
dt.register_event(
|
)
|
||||||
"HDRmerge", "view-changed",
|
HDRM.event_registered = true
|
||||||
function(event, old_view, new_view)
|
end
|
||||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
end
|
||||||
install_module()
|
|
||||||
end
|
script_data.destroy = destroy
|
||||||
end
|
script_data.restart = restart
|
||||||
)
|
script_data.destroy_method = "hide"
|
||||||
HDRM.event_registered = true
|
script_data.show = restart
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
script_data.restart = restart
|
|
||||||
script_data.destroy_method = "hide"
|
|
||||||
script_data.show = restart
|
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
@@ -52,23 +52,10 @@ local du = require "lib/dtutils"
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", "LabelsToTags")
|
du.check_min_api_version("7.0.0", "LabelsToTags")
|
||||||
|
|
||||||
local gettext = darktable.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("labels to tags"),
|
|
||||||
purpose = _("allows the mass-application of tags using color labels and ratings as a guide"),
|
|
||||||
author = "August Schwerdfeger (august@schwerdfeger.name)",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/LabelsToTags"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -81,7 +68,7 @@ local ltt = {}
|
|||||||
ltt.module_installed = false
|
ltt.module_installed = false
|
||||||
ltt.event_registered = false
|
ltt.event_registered = false
|
||||||
|
|
||||||
local LIB_ID = _("LabelsToTags")
|
local LIB_ID = "LabelsToTags"
|
||||||
|
|
||||||
-- Helper functions: BEGIN
|
-- Helper functions: BEGIN
|
||||||
|
|
||||||
@@ -122,23 +109,23 @@ end
|
|||||||
|
|
||||||
|
|
||||||
local initialAvailableMappings = {
|
local initialAvailableMappings = {
|
||||||
[_("colors")] = { ["+*****"] = { _("red") },
|
["Colors"] = { ["+*****"] = { "Red" },
|
||||||
["*+****"] = { _("yellow") },
|
["*+****"] = { "Yellow" },
|
||||||
["**+***"] = { _("green") },
|
["**+***"] = { "Green" },
|
||||||
["***+**"] = { _("blue") },
|
["***+**"] = { "Blue" },
|
||||||
["****+*"] = { _("purple") } },
|
["****+*"] = { "Purple" } },
|
||||||
[_("single colors")] = { ["+----*"] = { _("red"), _("only red") },
|
["Single colors"] = { ["+----*"] = { "Red", "Only red" },
|
||||||
["-+---*"] = { _("yellow"), _("only yellow") },
|
["-+---*"] = { "Yellow", "Only yellow" },
|
||||||
["--+--*"] = { _("green"), _("only green") },
|
["--+--*"] = { "Green", "Only green" },
|
||||||
["---+-*"] = { _("blue"), _("only blue") },
|
["---+-*"] = { "Blue", "Only blue" },
|
||||||
["----+*"] = { _("purple"), _("only purple") } },
|
["----+*"] = { "Purple", "Only purple" } },
|
||||||
[_("ratings")] = { ["*****0"] = { _("no stars"), _("not rejected") },
|
["Ratings"] = { ["*****0"] = { "No stars", "Not rejected" },
|
||||||
["*****1"] = { _("one star"), _("not rejected") },
|
["*****1"] = { "One star", "Not rejected" },
|
||||||
["*****2"] = { _("two stars"), _("not rejected") },
|
["*****2"] = { "Two stars", "Not rejected" },
|
||||||
["*****3"] = { _("three stars"), _("not rejected") },
|
["*****3"] = { "Three stars", "Not rejected" },
|
||||||
["*****4"] = { _("four stars"), _("not rejected") },
|
["*****4"] = { "Four stars", "Not rejected" },
|
||||||
["*****5"] = { _("five stars"), _("not rejected") },
|
["*****5"] = { "Five stars", "Not rejected" },
|
||||||
["*****R"] = { _("rejected") } }
|
["*****R"] = { "Rejected" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
local availableMappings = {}
|
local availableMappings = {}
|
||||||
@@ -153,14 +140,14 @@ end
|
|||||||
|
|
||||||
local function getComboboxTooltip()
|
local function getComboboxTooltip()
|
||||||
if availableMappings == nil or next(availableMappings) == nil then
|
if availableMappings == nil or next(availableMappings) == nil then
|
||||||
return(_("no registered mappings -- using defaults"))
|
return("No registered mappings -- using defaults")
|
||||||
else
|
else
|
||||||
return(_("select a label-to-tag mapping"))
|
return("Select a label-to-tag mapping")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local mappingComboBox = darktable.new_widget("combobox"){
|
local mappingComboBox = darktable.new_widget("combobox"){
|
||||||
label = _("mapping"),
|
label = "mapping",
|
||||||
value = 1,
|
value = 1,
|
||||||
tooltip = getComboboxTooltip(),
|
tooltip = getComboboxTooltip(),
|
||||||
reset_callback = function(selfC)
|
reset_callback = function(selfC)
|
||||||
@@ -183,7 +170,7 @@ local mappingComboBox = darktable.new_widget("combobox"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
local function doTagging(selfC)
|
local function doTagging(selfC)
|
||||||
local job = darktable.gui.create_job(string.format(_("labels to tags (%d image%s)"), #(darktable.gui.action_images), (#(darktable.gui.action_images) == 1 and "" or "s")), true)
|
local job = darktable.gui.create_job(string.format("labels to tags (%d image" .. (#(darktable.gui.action_images) == 1 and "" or "s") .. ")",#(darktable.gui.action_images)),true)
|
||||||
job.percent = 0.0
|
job.percent = 0.0
|
||||||
local pctIncrement = 1.0 / #(darktable.gui.action_images)
|
local pctIncrement = 1.0 / #(darktable.gui.action_images)
|
||||||
|
|
||||||
@@ -214,8 +201,8 @@ ltt.my_widget = darktable.new_widget("box") {
|
|||||||
orientation = "vertical",
|
orientation = "vertical",
|
||||||
mappingComboBox,
|
mappingComboBox,
|
||||||
darktable.new_widget("button") {
|
darktable.new_widget("button") {
|
||||||
label = _("start"),
|
label = "start",
|
||||||
tooltip = _("tag all selected images"),
|
tooltip = "Tag all selected images",
|
||||||
clicked_callback = doTagging
|
clicked_callback = doTagging
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,7 +232,7 @@ end
|
|||||||
|
|
||||||
local function install_module()
|
local function install_module()
|
||||||
if not ltt.module_installed then
|
if not ltt.module_installed then
|
||||||
darktable.register_lib(LIB_ID,_("labels to tags"),true,true,{
|
darktable.register_lib(LIB_ID,"labels to tags",true,true,{
|
||||||
[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",20},
|
[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",20},
|
||||||
},ltt.my_widget,nil,nil)
|
},ltt.my_widget,nil,nil)
|
||||||
ltt.module_installed = true
|
ltt.module_installed = true
|
||||||
|
|||||||
@@ -1,261 +1,238 @@
|
|||||||
--[[
|
--[[
|
||||||
OpenInExplorer plugin for darktable
|
OpenInExplorer plugin for darktable
|
||||||
|
|
||||||
copyright (c) 2018 Kevin Ertel
|
copyright (c) 2018 Kevin Ertel
|
||||||
Update 2020 and macOS support by Volker Lenhardt
|
Update 2020 and macOS support by Volker Lenhardt
|
||||||
Linux support 2020 by Bill Ferguson
|
Linux support 2020 by Bill Ferguson
|
||||||
|
|
||||||
darktable is free software: you can redistribute it and/or modify
|
darktable is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
darktable is distributed in the hope that it will be useful,
|
darktable is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
--[[About this plugin
|
--[[About this plugin
|
||||||
This plugin adds the module "OpenInExplorer" to darktable's lighttable view.
|
This plugin adds the module "OpenInExplorer" to darktable's lighttable view.
|
||||||
|
|
||||||
----REQUIRED SOFTWARE----
|
----REQUIRED SOFTWARE----
|
||||||
Apple macOS, Microsoft Windows or Linux
|
Apple macOS, Microsoft Windows or Linux
|
||||||
|
|
||||||
----USAGE----
|
----USAGE----
|
||||||
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts )
|
Install: (see here for more detail: https://github.com/darktable-org/lua-scripts )
|
||||||
1) Copy this file into your "lua/contrib" folder where all other scripts reside.
|
1) Copy this file into your "lua/contrib" folder where all other scripts reside.
|
||||||
2) Require this file in your luarc file, as with any other dt plug-in
|
2) Require this file in your luarc file, as with any other dt plug-in
|
||||||
|
|
||||||
Select the photo(s) you wish to find in your operating system's file manager and press "show in file explorer" in the "selected images" section.
|
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.
|
- 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?).
|
- 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.
|
- 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.
|
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.
|
- 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.
|
- Cons: If you want to work with the files you are one step behind the original data.
|
||||||
|
|
||||||
----KNOWN ISSUES----
|
----KNOWN ISSUES----
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dsys = require "lib/dtutils.system"
|
local dsys = require "lib/dtutils.system"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
--Check API version
|
--Check API version
|
||||||
du.check_min_api_version("7.0.0", "OpenInExplorer")
|
du.check_min_api_version("7.0.0", "OpenInExplorer")
|
||||||
|
|
||||||
local function _(msgid)
|
-- return data structure for script_manager
|
||||||
return gettext(msgid)
|
|
||||||
end
|
local script_data = {}
|
||||||
|
|
||||||
-- return data structure for script_manager
|
script_data.destroy = nil -- function to destory the script
|
||||||
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
local script_data = {}
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
script_data.metadata = {
|
|
||||||
name = _("open in explorer"),
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
purpose = _("open a selected file in the system file manager"),
|
gettext.bindtextdomain("OpenInExplorer",dt.configuration.config_dir.."/lua/locale/")
|
||||||
author = "Kevin Ertel",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/OpenInExplorer"
|
local function _(msgid)
|
||||||
}
|
return gettext.dgettext("OpenInExplorer", msgid)
|
||||||
|
end
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
local act_os = dt.configuration.running_os
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
local PS = act_os == "windows" and "\\" or "/"
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
--Detect OS and quit if it is not supported.
|
||||||
local act_os = dt.configuration.running_os
|
if act_os ~= "macos" and act_os ~= "windows" and act_os ~= "linux" then
|
||||||
local PS = act_os == "windows" and "\\" or "/"
|
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")
|
||||||
--Detect OS and quit if it is not supported.
|
return
|
||||||
if act_os ~= "macos" and act_os ~= "windows" and act_os ~= "linux" then
|
end
|
||||||
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")
|
local use_links, links_dir = false, ""
|
||||||
return
|
if act_os ~= "windows" then
|
||||||
end
|
use_links = dt.preferences.read("OpenInExplorer", "use_links", "bool")
|
||||||
|
links_dir = dt.preferences.read("OpenInExplorer", "linked_image_files_dir", "string")
|
||||||
local use_links, links_dir = false, ""
|
end
|
||||||
if act_os ~= "windows" then
|
|
||||||
use_links = dt.preferences.read("OpenInExplorer", "use_links", "bool")
|
--Check if the directory exists that was chosen for the file links. Return boolean.
|
||||||
links_dir = dt.preferences.read("OpenInExplorer", "linked_image_files_dir", "string")
|
local function check_if_links_dir_exists()
|
||||||
end
|
local dir_exists = true
|
||||||
|
if not links_dir then
|
||||||
--Check if the directory exists that was chosen for the file links. Return boolean.
|
--Just for paranoic reasons. I tried, but I couldn't devise a setting for a nil value.
|
||||||
local function check_if_links_dir_exists()
|
dt.print(_("No links directory selected.\nPlease check the dt preferences (lua options)"))
|
||||||
local dir_exists = true
|
dt.print_error("OpenInExplorer: No links directory selected")
|
||||||
if not links_dir then
|
dir_exists = false
|
||||||
--Just for paranoic reasons. I tried, but I couldn't devise a setting for a nil value.
|
elseif not df.check_if_file_exists(links_dir) then
|
||||||
dt.print(_("no links directory selected\nplease check the dt preferences (lua options)"))
|
dt.print(string.format(_("Links directory '%s' not found.\nPlease check the dt preferences (lua options)"), links_dir))
|
||||||
dt.print_error("OpenInExplorer: No links directory selected")
|
dt.print_error(string.format("OpenInExplorer: Links directory '%s' not found", links_dir))
|
||||||
dir_exists = false
|
dir_exists = false
|
||||||
elseif not df.check_if_file_exists(links_dir) then
|
end
|
||||||
dt.print(string.format(_("links directory '%s' not found\nplease check the dt preferences (lua options)"), links_dir))
|
return dir_exists
|
||||||
dt.print_error(string.format("OpenInExplorer: Links directory '%s' not found", links_dir))
|
end
|
||||||
dir_exists = false
|
|
||||||
end
|
--Format strings for the commands to open the corresponding OS's file manager.
|
||||||
return dir_exists
|
local open_dir = {}
|
||||||
end
|
open_dir.windows = "explorer.exe /n, %s"
|
||||||
|
open_dir.macos = "open %s"
|
||||||
--Format strings for the commands to open the corresponding OS's file manager.
|
open_dir.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowFolders ass 1 %s ""]]
|
||||||
local open_dir = {}
|
|
||||||
open_dir.windows = "explorer.exe /n, %s"
|
local open_files = {}
|
||||||
open_dir.macos = "open %s"
|
open_files.windows = "explorer.exe /select, %s"
|
||||||
open_dir.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowFolders ass 1 %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 ""]]
|
||||||
local open_files = {}
|
|
||||||
open_files.windows = "explorer.exe /select, %s"
|
--Call the file mangager for each selected image on Linux.
|
||||||
open_files.macos = "osascript -e 'tell application \"Finder\" to (reveal {%s}) activate'"
|
--There is one call to busctl containing a list of all the image file names.
|
||||||
open_files.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowItems ass %d %s ""]]
|
local function call_list_of_files(selected_images)
|
||||||
|
local current_image, file_uris, run_cmd = "", "", ""
|
||||||
reveal_file_osx_cmd = "\"%s\" as POSIX file"
|
for _, image in pairs(selected_images) do
|
||||||
|
current_image = image.path..PS..image.filename
|
||||||
--Call the osx Finder with each selected image selected.
|
file_uris = file_uris .. df.sanitize_filename("file://" .. current_image) .. " "
|
||||||
--For images in multiple folders, Finder will open a separate window for each
|
dt.print_log("file_uris is " .. file_uris)
|
||||||
local function call_list_of_files_osx(selected_images)
|
end
|
||||||
local cmds = {}
|
run_cmd = string.format(open_files.linux, #selected_images, file_uris)
|
||||||
for _, image in pairs(selected_images) do
|
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
||||||
current_image = image.path..PS..image.filename
|
dsys.external_command(run_cmd)
|
||||||
-- AppleScript needs double quoted strings, and the whole command is wrapped in single quotes.
|
end
|
||||||
table.insert(cmds, string.format(reveal_file_osx_cmd, string.gsub(string.gsub(current_image, "\"", "\\\""), "'", "'\"'\"'")))
|
|
||||||
end
|
--Call the file manager for each selected image on Windows and macOS.
|
||||||
reveal_cmd = table.concat(cmds, ",")
|
local function call_file_by_file(selected_images)
|
||||||
run_cmd = string.format(open_files.macos, reveal_cmd)
|
local current_image, run_cmd = "", ""
|
||||||
dt.print_log("OSX run_cmd = "..run_cmd)
|
for _, image in pairs(selected_images) do
|
||||||
dsys.external_command(run_cmd)
|
current_image = image.path..PS..image.filename
|
||||||
end
|
run_cmd = string.format(open_files[act_os], df.sanitize_filename(current_image))
|
||||||
|
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
||||||
--Call the file mangager for each selected image on Linux.
|
dsys.external_command(run_cmd)
|
||||||
--There is one call to busctl containing a list of all the image file names.
|
end
|
||||||
local function call_list_of_files(selected_images)
|
end
|
||||||
local current_image, file_uris, run_cmd = "", "", ""
|
|
||||||
for _, image in pairs(selected_images) do
|
--Create a link for each selected image, and finally call the file manager.
|
||||||
current_image = image.path..PS..image.filename
|
local function set_links(selected_images)
|
||||||
file_uris = file_uris .. df.sanitize_filename("file://" .. current_image) .. " "
|
local current_image, link_target, run_cmd, k = "", "", "", nil
|
||||||
dt.print_log("file_uris is " .. file_uris)
|
for k, image in pairs(selected_images) do
|
||||||
end
|
current_image = image.path..PS..image.filename
|
||||||
run_cmd = string.format(open_files.linux, #selected_images, file_uris)
|
link_target = df.create_unique_filename(links_dir .. PS .. image.filename)
|
||||||
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
run_cmd = string.format("ln -s %s %s", df.sanitize_filename(current_image), df.sanitize_filename(link_target))
|
||||||
dsys.external_command(run_cmd)
|
--[[
|
||||||
end
|
In case Windows will allow normal users to create soft links:
|
||||||
|
if act_os == "windows" then
|
||||||
--Call the file manager for each selected image on Windows and macOS.
|
run_cmd = string.format("mklink %s %s", df.sanitize_filename(link_target), df.sanitize_filename(current_image))
|
||||||
local function call_file_by_file(selected_images)
|
end
|
||||||
local current_image, run_cmd = "", ""
|
]]
|
||||||
for _, image in pairs(selected_images) do
|
if dsys.external_command(run_cmd) ~= 0 then
|
||||||
current_image = image.path..PS..image.filename
|
dt.print(_("Failed to create links. Missing rights?"))
|
||||||
run_cmd = string.format(open_files[act_os], df.sanitize_filename(current_image))
|
dt.print_error("OpenInExplorer: Failed to create links")
|
||||||
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
return
|
||||||
dsys.external_command(run_cmd)
|
end
|
||||||
end
|
end
|
||||||
end
|
--The URI format is necessary only for the Linux busctl command.
|
||||||
|
--But it is accepted by the Windows Explorer and macOS's Finder all the same.
|
||||||
--Create a link for each selected image, and finally call the file manager.
|
run_cmd = string.format(open_dir[act_os], df.sanitize_filename("file://"..links_dir))
|
||||||
local function set_links(selected_images)
|
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
||||||
local current_image, link_target, run_cmd, k = "", "", "", nil
|
dsys.external_command(run_cmd)
|
||||||
for k, image in pairs(selected_images) do
|
end
|
||||||
current_image = image.path..PS..image.filename
|
|
||||||
link_target = df.create_unique_filename(links_dir .. PS .. image.filename)
|
--The working function that starts the particular task.
|
||||||
run_cmd = string.format("ln -s %s %s", df.sanitize_filename(current_image), df.sanitize_filename(link_target))
|
local function open_in_fmanager()
|
||||||
--[[
|
local images = dt.gui.selection()
|
||||||
In case Windows will allow normal users to create soft links:
|
if #images == 0 then
|
||||||
if act_os == "windows" then
|
dt.print(_("Please select an image"))
|
||||||
run_cmd = string.format("mklink %s %s", df.sanitize_filename(link_target), df.sanitize_filename(current_image))
|
else
|
||||||
end
|
if use_links and not check_if_links_dir_exists() then
|
||||||
]]
|
return
|
||||||
if dsys.external_command(run_cmd) ~= 0 then
|
end
|
||||||
dt.print(_("failed to create links, missing rights?"))
|
if #images > 15 and not use_links then
|
||||||
dt.print_error("OpenInExplorer: Failed to create links")
|
dt.print(_("Please select fewer images (max. 15)"))
|
||||||
return
|
elseif use_links then
|
||||||
end
|
set_links(images)
|
||||||
end
|
else
|
||||||
--The URI format is necessary only for the Linux busctl command.
|
if act_os == "linux" then
|
||||||
--But it is accepted by the Windows Explorer and macOS's Finder all the same.
|
call_list_of_files(images)
|
||||||
run_cmd = string.format(open_dir[act_os], df.sanitize_filename("file://"..links_dir))
|
else
|
||||||
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
call_file_by_file(images)
|
||||||
dsys.external_command(run_cmd)
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
--The working function that starts the particular task.
|
end
|
||||||
local function open_in_fmanager()
|
|
||||||
local images = dt.gui.selection()
|
local function destroy()
|
||||||
if #images == 0 then
|
dt.gui.libs.image.destroy_action("OpenInExplorer")
|
||||||
dt.print(_("please select an image"))
|
dt.destroy_event("OpenInExplorer", "shortcut")
|
||||||
else
|
if act_os ~= "windows" then
|
||||||
if use_links and not check_if_links_dir_exists() then
|
dt.preferences.destroy("OpenInExplorer", "linked_image_files_dir")
|
||||||
return
|
end
|
||||||
end
|
dt.preferences.destroy("OpenInExplorer", "use_links")
|
||||||
if #images > 15 and not use_links then
|
end
|
||||||
dt.print(_("please select fewer images (max. 15)"))
|
|
||||||
elseif use_links then
|
|
||||||
set_links(images)
|
-- GUI --
|
||||||
else
|
dt.gui.libs.image.register_action(
|
||||||
if act_os == "linux" then
|
"OpenInExplorer", _("show in file explorer"),
|
||||||
call_list_of_files(images)
|
function() open_in_fmanager() end,
|
||||||
elseif act_os == "macos" then
|
_("Open the file manager at the selected image's location")
|
||||||
call_list_of_files_osx(images)
|
)
|
||||||
else
|
|
||||||
call_file_by_file(images)
|
|
||||||
end
|
if act_os ~= "windows" then
|
||||||
end
|
dt.preferences.register("OpenInExplorer", "linked_image_files_dir", -- name
|
||||||
end
|
"directory", -- type
|
||||||
end
|
_("OpenInExplorer: linked files directory"), -- label
|
||||||
|
_("Directory to store the links to the file names. Requires restart to take effect"), -- tooltip
|
||||||
local function destroy()
|
"Links to image files", -- default
|
||||||
dt.gui.libs.image.destroy_action("OpenInExplorer")
|
dt.new_widget("file_chooser_button"){
|
||||||
dt.destroy_event("OpenInExplorer", "shortcut")
|
title = _("Select directory"),
|
||||||
if act_os ~= "windows" then
|
is_directory = true,
|
||||||
dt.preferences.destroy("OpenInExplorer", "linked_image_files_dir")
|
}
|
||||||
end
|
)
|
||||||
dt.preferences.destroy("OpenInExplorer", "use_links")
|
dt.preferences.register("OpenInExplorer", "use_links", -- name
|
||||||
end
|
"bool", -- type
|
||||||
|
_("OpenInExplorer: use links"), -- label
|
||||||
|
_("Use links instead of multiple windows. Requires restart to take effect"), -- tooltip
|
||||||
-- GUI --
|
false, -- default
|
||||||
dt.gui.libs.image.register_action(
|
""
|
||||||
"OpenInExplorer", _("show in file explorer"),
|
)
|
||||||
function() open_in_fmanager() end,
|
end
|
||||||
_("open the file manager at the selected image's location")
|
|
||||||
)
|
dt.register_event(
|
||||||
|
"OpenInExplorer", "shortcut",
|
||||||
|
function(event, shortcut) open_in_fmanager() end,
|
||||||
if act_os ~= "windows" then
|
"OpenInExplorer"
|
||||||
dt.preferences.register("OpenInExplorer", "linked_image_files_dir", -- name
|
)
|
||||||
"directory", -- type
|
|
||||||
_("OpenInExplorer: linked files directory"), -- label
|
script_data.destroy = destroy
|
||||||
_("directory to store the links to the file names, requires restart to take effect"), -- tooltip
|
|
||||||
"Links to image files", -- default
|
return script_data
|
||||||
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(
|
|
||||||
"OpenInExplorer", "shortcut",
|
|
||||||
function(event, shortcut) open_in_fmanager() end,
|
|
||||||
"OpenInExplorer"
|
|
||||||
)
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
|
|||||||
@@ -65,24 +65,10 @@ local MODULE_NAME = "RL_out_sharp"
|
|||||||
-- check API version
|
-- check API version
|
||||||
du.check_min_api_version("7.0.0", MODULE_NAME)
|
du.check_min_api_version("7.0.0", MODULE_NAME)
|
||||||
|
|
||||||
-- translation
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("RL output sharpening"),
|
|
||||||
purpose = _("Richardson-Lucy output sharpening using GMic"),
|
|
||||||
author = "Marco Carrarini <marco.carrarini@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/RL_out_sharp"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -91,24 +77,20 @@ script_data.show = nil -- only required for libs since the destroy_method only h
|
|||||||
-- OS compatibility
|
-- OS compatibility
|
||||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
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
|
-- initialize module preferences
|
||||||
if not dt.preferences.read(MODULE_NAME, "initialized", "bool") then
|
if not dt.preferences.read(MODULE_NAME, "initialized", "bool") then
|
||||||
dt.preferences.write(MODULE_NAME, "sigma", "string", "0.7")
|
dt.preferences.write(MODULE_NAME, "sigma", "string", "0.7")
|
||||||
dt.preferences.write(MODULE_NAME, "iterations", "string", "10")
|
dt.preferences.write(MODULE_NAME, "iterations", "string", "10")
|
||||||
dt.preferences.write(MODULE_NAME, "jpg_quality", "string", "95")
|
dt.preferences.write(MODULE_NAME, "jpg_quality", "string", "95")
|
||||||
dt.preferences.write(MODULE_NAME, "initialized", "bool", true)
|
dt.preferences.write(MODULE_NAME, "initialized", "bool", true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- preserve original image metadata in the output image -----------------------
|
|
||||||
local function preserve_metadata(original, sharpened)
|
|
||||||
local exiftool = df.check_if_bin_exists("exiftool")
|
|
||||||
|
|
||||||
if exiftool then
|
|
||||||
dtsys.external_command("exiftool -overwrite_original_in_place -tagsFromFile " .. original .. " " .. sharpened)
|
|
||||||
else
|
|
||||||
dt.print_log(MODULE_NAME .. " exiftool not found, metadata not preserved")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- setup export ---------------------------------------------------------------
|
-- setup export ---------------------------------------------------------------
|
||||||
@@ -155,7 +137,7 @@ local function export2RL(storage, image_table, extra_data)
|
|||||||
for image, temp_name in pairs(image_table) do
|
for image, temp_name in pairs(image_table) do
|
||||||
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
dt.print(string.format(_("sharpening image %d ..."), i))
|
dt.print(_("sharpening image ")..i.." ...")
|
||||||
-- create unique filename
|
-- create unique filename
|
||||||
new_name = output_folder..PS..df.get_basename(temp_name)..".jpg"
|
new_name = output_folder..PS..df.get_basename(temp_name)..".jpg"
|
||||||
new_name = df.create_unique_filename(new_name)
|
new_name = df.create_unique_filename(new_name)
|
||||||
@@ -172,10 +154,7 @@ local function export2RL(storage, image_table, extra_data)
|
|||||||
if result ~= 0 then
|
if result ~= 0 then
|
||||||
dt.print(_("sharpening error"))
|
dt.print(_("sharpening error"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- copy metadata from input_file to output_file
|
|
||||||
preserve_metadata(input_file, output_file)
|
|
||||||
|
|
||||||
-- delete temp image
|
-- delete temp image
|
||||||
os.remove(temp_name)
|
os.remove(temp_name)
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
auto_snapshot.lua - automatically take a snapshot when an image is loaded in darkroom
|
|
||||||
|
|
||||||
Copyright (C) 2024 Bill Ferguson <wpferguson@gamil.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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
auto_snapshot -
|
|
||||||
|
|
||||||
automatically take a snapshot when an image is loaded in darkroom
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
* start the script from script_manager
|
|
||||||
* open an image in darkroom
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
Bill Ferguson <wpferguson@gamil.com>
|
|
||||||
|
|
||||||
CHANGES
|
|
||||||
]]
|
|
||||||
|
|
||||||
local dt = require "darktable"
|
|
||||||
local du = require "lib/dtutils"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "auto_snapshot"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.error
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("auto snapshot"), -- name of script
|
|
||||||
purpose = _("automatically take a snapshot when an image is loaded in darkroom"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gamil.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/auto_snapshot/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local auto_snapshot = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.preferences.register(MODULE, "always_create_snapshot", "bool", "auto_snapshot - " .. _("always automatically create_snapshot"),
|
|
||||||
_("create a snapshot even if the image is altered"), false)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.destroy_event(MODULE, "darkroom-image-loaded")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "darkroom-image-loaded",
|
|
||||||
function(event, clean, image)
|
|
||||||
local always = dt.preferences.read(MODULE, "always_create_snapshot", "bool")
|
|
||||||
if clean and always then
|
|
||||||
dt.gui.libs.snapshots.take_snapshot()
|
|
||||||
elseif clean and not image.is_altered then
|
|
||||||
dt.gui.libs.snapshots.take_snapshot()
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
auto_tag_modified.lua - automatically tag an image when it has been manually modified in darkroom
|
|
||||||
|
|
||||||
Copyright (C) 2025 Michael Reiger <michael@rauschpfeife.net>, modeled after auto_snapshot.lua by Bill Ferguson <wpferguson@gamil.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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
auto_tag_modified -
|
|
||||||
|
|
||||||
automatically tag an image when it has been manually modified in the darkroom, that is if its history has changed at the time it is closed from the darkroom.
|
|
||||||
(This will not catch applying styles from the light table though.)
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
* start the script from script_manager
|
|
||||||
* open an image in darkroom, and close it after making an operation that changes the history
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
Michael Reiger <michael@rauschpfeife.net>
|
|
||||||
|
|
||||||
CHANGES
|
|
||||||
]]
|
|
||||||
|
|
||||||
local dt = require "darktable"
|
|
||||||
local du = require "lib/dtutils"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "auto_tag_modified"
|
|
||||||
local DEFAULT_TAG_NAME <const> = "darktable manually modified"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.error
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("auto tag modified"), -- name of script
|
|
||||||
purpose = _("automatically tag an image when it has been manually modified in darkroom"), -- purpose of script
|
|
||||||
author = "Michael Reiger <michael@rauschpfeife.net>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/auto_tag_modified/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local auto_tag_modified = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.preferences.register(MODULE, "auto_modified_tag_name", "string", "auto_tag_modified - " .. _("tag name to use for auto tagging modified images"),
|
|
||||||
_("tag name to use for auto tagging modified images"), DEFAULT_TAG_NAME)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.destroy_event(MODULE, "darkroom-image-history-changed")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "darkroom-image-history-changed",
|
|
||||||
function(event, image)
|
|
||||||
local tag_name = dt.preferences.read(MODULE, "auto_modified_tag_name", "string")
|
|
||||||
local tag = dt.tags.create(tag_name)
|
|
||||||
dt.tags.attach(tag, image)
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -39,29 +39,13 @@ GPLv2
|
|||||||
local darktable = require "darktable"
|
local darktable = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local filelib = require "lib/dtutils.file"
|
local filelib = require "lib/dtutils.file"
|
||||||
local syslib = require "lib/dtutils.system"
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "autostyle")
|
du.check_min_api_version("7.0.0", "autostyle")
|
||||||
|
|
||||||
local gettext = darktable.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
local have_not_printed_config_message = true
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("auto style"),
|
|
||||||
purpose = _("automatically apply a style based on image EXIF tag"),
|
|
||||||
author = "Marc Cousin <cousinmarc@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/autostyle/"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -103,62 +87,56 @@ end
|
|||||||
local function autostyle_apply_one_image (image)
|
local function autostyle_apply_one_image (image)
|
||||||
|
|
||||||
local pref = darktable.preferences.read("autostyle", "exif_tag", "string")
|
local pref = darktable.preferences.read("autostyle", "exif_tag", "string")
|
||||||
|
|
||||||
if pref and string.len(pref) >= 6 then
|
|
||||||
-- We need the tag, the value and the style_name provided from the configuration string
|
-- We need the tag, the value and the style_name provided from the configuration string
|
||||||
local tag, value, style_name = string.match(pref, "(%g+)%s*=%s*([%g ]-)%s*=>%s*(%g+)")
|
local tag, value, style_name = string.match(pref, "(%g+)%s*=%s*(%g+)%s*=>%s*(%g+)")
|
||||||
|
|
||||||
-- check they all exist (correct syntax)
|
-- check they all exist (correct syntax)
|
||||||
if (not tag) then
|
if (not tag) then
|
||||||
darktable.print(string.format(_("EXIF tag not found in %s"), pref))
|
darktable.print("EXIF TAG not found in " .. pref)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if (not value) then
|
if (not value) then
|
||||||
darktable.print(string.format(_("value to match not found in %s"), pref))
|
darktable.print("value to match not found in " .. pref)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if (not style_name) then
|
if (not style_name) then
|
||||||
darktable.print(string.format(_("style name not found in %s"), pref))
|
darktable.print("style name not found in " .. pref)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if not filelib.check_if_bin_exists("exiftool") then
|
if not filelib.check_if_bin_exists("exiftool") then
|
||||||
darktable.print(_("can't find exiftool"))
|
darktable.print("Can't find exiftool")
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- First find the style (we have its name)
|
-- First find the style (we have its name)
|
||||||
local styles = darktable.styles
|
local styles = darktable.styles
|
||||||
local style
|
local style
|
||||||
for _, s in ipairs(styles) do
|
for _, s in ipairs(styles) do
|
||||||
if s.name == style_name then
|
if s.name == style_name then
|
||||||
style = s
|
style = s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if (not style) then
|
if (not style) then
|
||||||
darktable.print(string.format(_("style not found for autostyle: %s"), style_name))
|
darktable.print("style not found for autostyle: " .. style_name)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply the style to image, if it is tagged
|
-- Apply the style to image, if it is tagged
|
||||||
local ok, auto_dr_attr = pcall(exiftool_attribute, image.path .. '/' .. image.filename,tag)
|
local ok, auto_dr_attr = pcall(exiftool_attribute, image.path .. '/' .. image.filename,tag)
|
||||||
--darktable.print_error("dr_attr:" .. auto_dr_attr)
|
--darktable.print_error("dr_attr:" .. auto_dr_attr)
|
||||||
-- If the lookup fails, stop here
|
-- If the lookup fails, stop here
|
||||||
if (not ok) then
|
if (not ok) then
|
||||||
darktable.print(string.format(_("couldn't get attribute %s from exiftool's output"), auto_dr_attr))
|
darktable.print("Couldn't get attribute " .. auto_dr_attr .. " from exiftool's output")
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if auto_dr_attr == value then
|
if auto_dr_attr == value then
|
||||||
darktable.print_log("Image " .. image.filename .. ": autostyle automatically applied " .. pref)
|
darktable.print_log("Image " .. image.filename .. ": autostyle automatically applied " .. pref)
|
||||||
darktable.styles.apply(style,image)
|
darktable.styles.apply(style,image)
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
darktable.print_log("Image " .. image.filename .. ": autostyle not applied, exif tag " .. pref .. " not matched: " .. auto_dr_attr)
|
darktable.print_log("Image " .. image.filename .. ": autostyle not applied, exif tag " .. pref .. " not matched: " .. auto_dr_attr)
|
||||||
return 0
|
return 0
|
||||||
end
|
|
||||||
elseif have_not_printed_config_message then
|
|
||||||
have_not_printed_config_message = false
|
|
||||||
darktable.print(string.format(_("%s is not configured, please configure the preference in Lua options"), script_data.metadata.name))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -176,7 +154,7 @@ local function autostyle_apply(shortcut)
|
|||||||
images_submitted = images_submitted + 1
|
images_submitted = images_submitted + 1
|
||||||
images_processed = images_processed + autostyle_apply_one_image(image)
|
images_processed = images_processed + autostyle_apply_one_image(image)
|
||||||
end
|
end
|
||||||
darktable.print(string.format(_("applied auto style to %d out of %d image(s)"), images_processed, images_submitted))
|
darktable.print("Applied auto style to " .. images_processed .. " out of " .. images_submitted .. " image(s)")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function destroy()
|
local function destroy()
|
||||||
@@ -186,15 +164,13 @@ end
|
|||||||
|
|
||||||
-- Registering events
|
-- Registering events
|
||||||
darktable.register_event("autostyle", "shortcut", autostyle_apply,
|
darktable.register_event("autostyle", "shortcut", autostyle_apply,
|
||||||
_("apply your chosen style from exiftool tags"))
|
"Apply your chosen style from exiftool tags")
|
||||||
|
|
||||||
darktable.preferences.register("autostyle", "exif_tag", "string",
|
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", "")
|
||||||
string.format("%s: EXIF_tag=value=>style", script_data.metadata.name),
|
|
||||||
_("apply a style automatically if an EXIF tag matches value, find the tag with exiftool"), "")
|
|
||||||
|
|
||||||
darktable.register_event("autostyle", "post-import-image",
|
darktable.register_event("autostyle", "post-import-image",
|
||||||
autostyle_apply_one_image_event)
|
autostyle_apply_one_image_event)
|
||||||
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
return script_data
|
return script_data
|
||||||
@@ -36,34 +36,30 @@ USAGE
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
local MODULE = "change_group_leader"
|
local MODULE = "change_group_leader"
|
||||||
|
|
||||||
du.check_min_api_version("3.0.0", MODULE)
|
du.check_min_api_version("3.0.0", MODULE)
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("change group leader"),
|
|
||||||
purpose = _("automatically change the leader of raw+jpg paired image groups"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/change_group_leader"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain(MODULE, dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext(MODULE, msgid)
|
||||||
|
end
|
||||||
|
|
||||||
-- create a namespace to contain persistent data and widgets
|
-- create a namespace to contain persistent data and widgets
|
||||||
local chg_grp_ldr = {}
|
chg_grp_ldr = {}
|
||||||
|
|
||||||
local cgl = chg_grp_ldr
|
local cgl = chg_grp_ldr
|
||||||
|
|
||||||
@@ -80,7 +76,7 @@ local function install_module()
|
|||||||
if not cgl.module_installed then
|
if not cgl.module_installed then
|
||||||
dt.register_lib(
|
dt.register_lib(
|
||||||
MODULE, -- Module name
|
MODULE, -- Module name
|
||||||
_("change group leader"), -- Visible name
|
_("change_group_leader"), -- Visible name
|
||||||
true, -- expandable
|
true, -- expandable
|
||||||
false, -- resetable
|
false, -- resetable
|
||||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 700}}, -- containers
|
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 700}}, -- containers
|
||||||
@@ -125,7 +121,7 @@ end
|
|||||||
|
|
||||||
local function process_image_groups(images)
|
local function process_image_groups(images)
|
||||||
if #images < 1 then
|
if #images < 1 then
|
||||||
dt.print(_("no images selected"))
|
dt.print(_("No images selected."))
|
||||||
dt.print_log(MODULE .. "no images seletected, returning...")
|
dt.print_log(MODULE .. "no images seletected, returning...")
|
||||||
else
|
else
|
||||||
local mode = cgl.widgets.mode.value
|
local mode = cgl.widgets.mode.value
|
||||||
@@ -165,7 +161,7 @@ cgl.widgets.mode = dt.new_widget("combobox"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
cgl.widgets.execute = dt.new_widget("button"){
|
cgl.widgets.execute = dt.new_widget("button"){
|
||||||
label = _("execute"),
|
label = _("Execute"),
|
||||||
clicked_callback = function()
|
clicked_callback = function()
|
||||||
process_image_groups(dt.gui.action_images)
|
process_image_groups(dt.gui.action_images)
|
||||||
end
|
end
|
||||||
@@ -202,4 +198,4 @@ script_data.restart = restart
|
|||||||
script_data.destroy_method = "hide"
|
script_data.destroy_method = "hide"
|
||||||
script_data.show = restart
|
script_data.show = restart
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
@@ -39,33 +39,29 @@
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("clear GPS info"),
|
|
||||||
purpose = _("remove GPS data from selected image(s)"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/clear_gps/"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
local gettext = dt.gettext
|
||||||
|
|
||||||
-- not a number
|
-- not a number
|
||||||
local NaN = 0/0
|
local NaN = 0/0
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "clear_GPS")
|
du.check_min_api_version("7.0.0", "clear_GPS")
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("clear_GPS",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("clear_GPS", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local function clear_GPS(images)
|
local function clear_GPS(images)
|
||||||
for _, image in ipairs(images) do
|
for _, image in ipairs(images) do
|
||||||
-- set the location information to Not a Number (NaN) so it displays correctly
|
-- set the location information to Not a Number (NaN) so it displays correctly
|
||||||
@@ -85,13 +81,13 @@ script_data.destroy = destroy
|
|||||||
dt.gui.libs.image.register_action(
|
dt.gui.libs.image.register_action(
|
||||||
"clear_GPS", _("clear GPS data"),
|
"clear_GPS", _("clear GPS data"),
|
||||||
function(event, images) clear_GPS(images) end,
|
function(event, images) clear_GPS(images) end,
|
||||||
_("clear GPS data from selected images")
|
_("Clear GPS data from selected images")
|
||||||
)
|
)
|
||||||
|
|
||||||
dt.register_event(
|
dt.register_event(
|
||||||
"clear_GPS", "shortcut",
|
"clear_GPS", "shortcut",
|
||||||
function(event, shortcut) clear_GPS(dt.gui.action_images) end,
|
function(event, shortcut) clear_GPS(dt.gui.action_images) end,
|
||||||
_("clear GPS data from selected images")
|
_("Clear GPS data")
|
||||||
)
|
)
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -45,7 +45,6 @@
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dtsys = require "lib/dtutils.system"
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "color_profile_manager")
|
du.check_min_api_version("7.0.0", "color_profile_manager")
|
||||||
|
|
||||||
@@ -53,10 +52,13 @@ du.check_min_api_version("7.0.0", "color_profile_manager")
|
|||||||
-- L O C A L I Z A T I O N
|
-- L O C A L I Z A T I O N
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("color_profile_manager", dt.configuration.config_dir .. "/lua/locale/")
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext("color_profile_manager", msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@@ -117,13 +119,13 @@ end
|
|||||||
|
|
||||||
local function add_profile(file, dir)
|
local function add_profile(file, dir)
|
||||||
df.file_copy(file, dir)
|
df.file_copy(file, dir)
|
||||||
dt.print(string.format(_("added color profile %s to %s"), file, dir))
|
dt.print(_("added color profile " .. file .. " to " .. dir))
|
||||||
dt.print_log("color profile " .. file .. " added to " .. dir)
|
dt.print_log("color profile " .. file .. " added to " .. dir)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function remove_profile(file, dir)
|
local function remove_profile(file, dir)
|
||||||
os.remove(dir .. PS .. file)
|
os.remove(dir .. PS .. file)
|
||||||
dt.print(string.format(_("removed color profile %s from %s"), file, dir))
|
dt.print(_("removed color profile " .. file .. " from " .. dir))
|
||||||
dt.print_log("color profile " .. file .. " removed from " .. dir)
|
dt.print_log("color profile " .. file .. " removed from " .. dir)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -362,13 +364,6 @@ end
|
|||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("color profile manager"),
|
|
||||||
purpose = _("manage external darktable color profiles"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/color_profile_manager"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
script_data.restart = restart
|
script_data.restart = restart
|
||||||
script_data.destroy_method = "hide"
|
script_data.destroy_method = "hide"
|
||||||
|
|||||||
@@ -40,30 +40,26 @@ local dt = require "darktable"
|
|||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local debug = require "darktable.debug"
|
local debug = require "darktable.debug"
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "copy_attach_detach_tags")
|
du.check_min_api_version("7.0.0", "copy_attach_detach_tags")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("copy attach detach tags"),
|
|
||||||
purpose = _("shortcuts to copy, paste, replace, or remove tags from images"),
|
|
||||||
author = "Christian Kanzian",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/copy_attach_detach_tags"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("copy_attach_detach_tags",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("copy_attach_detach_tags", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local cadt = {}
|
local cadt = {}
|
||||||
cadt.module_installed = false
|
cadt.module_installed = false
|
||||||
cadt.event_registered = false
|
cadt.event_registered = false
|
||||||
@@ -103,7 +99,7 @@ local function mcopy_tags()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
dt.print(_('image tags copied ...'))
|
dt.print(_('Image tags copied ...'))
|
||||||
|
|
||||||
--create UI tag list
|
--create UI tag list
|
||||||
local taglist = ""
|
local taglist = ""
|
||||||
@@ -125,7 +121,7 @@ local function mcopy_tags()
|
|||||||
local function attach_tags()
|
local function attach_tags()
|
||||||
|
|
||||||
if next(image_tags) == nil then
|
if next(image_tags) == nil then
|
||||||
dt.print(_('no tag to attach, please copy tags first.'))
|
dt.print(_('No tag to attach, please copy tags first.'))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -149,7 +145,7 @@ local function attach_tags()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
dt.print(_('tags attached ...'))
|
dt.print(_('Tags attached ...'))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function detach_tags()
|
local function detach_tags()
|
||||||
@@ -165,13 +161,13 @@ local function detach_tags()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
dt.print(_('tags removed from image(s).'))
|
dt.print(_('Tags removed from image(s).'))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function replace_tags()
|
local function replace_tags()
|
||||||
detach_tags()
|
detach_tags()
|
||||||
attach_tags()
|
attach_tags()
|
||||||
dt.print(_('tags replaced'))
|
dt.print(_('Tags replaced'))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function install_module()
|
local function install_module()
|
||||||
|
|||||||
@@ -37,23 +37,10 @@ local du = require "lib/dtutils"
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", "cr2hdr")
|
du.check_min_api_version("7.0.0", "cr2hdr")
|
||||||
|
|
||||||
local gettext = darktable.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("cr2hdr"),
|
|
||||||
purpose = _("process Magic Lantern dual ISO images"),
|
|
||||||
author = "Till Theato <theato@ttill.de>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/cr2hdr"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -100,7 +87,7 @@ end
|
|||||||
local function convert_images()
|
local function convert_images()
|
||||||
if next(queue) == nil then return end
|
if next(queue) == nil then return end
|
||||||
|
|
||||||
job = darktable.gui.create_job(_("dual ISO conversion"), true, stop_conversion)
|
job = darktable.gui.create_job("Dual ISO conversion", true, stop_conversion)
|
||||||
for key,image in pairs(queue) do
|
for key,image in pairs(queue) do
|
||||||
if job.valid then
|
if job.valid then
|
||||||
job.percent = (key-1)/#queue
|
job.percent = (key-1)/#queue
|
||||||
@@ -111,7 +98,7 @@ local function convert_images()
|
|||||||
end
|
end
|
||||||
local success_count = 0
|
local success_count = 0
|
||||||
for _ in pairs(processed_files) do success_count = success_count + 1 end
|
for _ in pairs(processed_files) do success_count = success_count + 1 end
|
||||||
darktable.print(string.format(_("dual ISO conversion successful on %d/%d images."), success_count, #queue))
|
darktable.print("Dual ISO conversion successful on " .. success_count .. "/" .. #queue .. " images.")
|
||||||
job.valid = false
|
job.valid = false
|
||||||
processed_files = {}
|
processed_files = {}
|
||||||
queue = {}
|
queue = {}
|
||||||
@@ -135,13 +122,13 @@ local function destroy()
|
|||||||
end
|
end
|
||||||
|
|
||||||
darktable.register_event("cr2hdr", "shortcut",
|
darktable.register_event("cr2hdr", "shortcut",
|
||||||
convert_action_images, _("run cr2hdr (Magic Lantern DualISO converter) on selected images"))
|
convert_action_images, "Run cr2hdr (Magic Lantern DualISO converter) on selected images")
|
||||||
darktable.register_event("cr2hdr", "post-import-image",
|
darktable.register_event("cr2hdr", "post-import-image",
|
||||||
file_imported)
|
file_imported)
|
||||||
darktable.register_event("cr2hdr", "post-import-film",
|
darktable.register_event("cr2hdr", "post-import-film",
|
||||||
film_imported)
|
film_imported)
|
||||||
|
|
||||||
darktable.preferences.register("cr2hdr", "onimport", "bool", _("invoke on import"), _("if true then cr2hdr will try to proccess every file during importing\nwarning: cr2hdr is quite slow even in figuring out on whether the file is dual ISO or not."), false)
|
darktable.preferences.register("cr2hdr", "onimport", "bool", "Invoke on import", "If true then cr2hdr will try to proccess every file during importing. Warning: cr2hdr is quite slow even in figuring out on whether the file is Dual ISO or not.", false)
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
--[[
|
--[[
|
||||||
|
|
||||||
cycle_group_leader.lua - change image group leader
|
cycle_group_leader.lua - change image grouip leader
|
||||||
|
|
||||||
Copyright (C) 2024 Bill Ferguson <wpferguson@gmail.com>
|
Copyright (C) 2024 Bill Ferguson <wpferguson@gmail.com>
|
||||||
|
|
||||||
@@ -54,34 +54,30 @@ local MODULE = "cycle_group_leader"
|
|||||||
du.check_min_api_version("7.0.0", MODULE)
|
du.check_min_api_version("7.0.0", MODULE)
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("cycle group leader"),
|
|
||||||
purpose = _("change image group leader"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/cycle_group_leader"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
-- I 1 8 N
|
||||||
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain(MODULE, dt.configuration.config_dir .. "/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext(MODULE, msgid)
|
||||||
|
end
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- F U N C T I O N S
|
-- F U N C T I O N S
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@@ -104,7 +100,7 @@ end
|
|||||||
local function cycle_group_leader(image)
|
local function cycle_group_leader(image)
|
||||||
local group_images = image:get_group_members()
|
local group_images = image:get_group_members()
|
||||||
if #group_images < 2 then
|
if #group_images < 2 then
|
||||||
hinter_msg(_("no images to cycle through in group"))
|
hinter_msg(_("No images to cycle to in group"))
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
local position = nil
|
local position = nil
|
||||||
@@ -151,7 +147,7 @@ dt.register_event(MODULE, "shortcut",
|
|||||||
-- ignore the film roll, it contains all the images, not just the imported
|
-- ignore the film roll, it contains all the images, not just the imported
|
||||||
local images = dt.gui.selection()
|
local images = dt.gui.selection()
|
||||||
if #images < 1 then
|
if #images < 1 then
|
||||||
dt.print(_("no image selected, please select an image and try again"))
|
dt.print(_("No image selected. Please select an image and try again"))
|
||||||
else
|
else
|
||||||
cycle_group_leader(images[1])
|
cycle_group_leader(images[1])
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
dbmaint.lua - perform database maintenance
|
dbmaint.lua - perform database maintenance
|
||||||
|
|
||||||
Copyright (C) 2024 Bill Ferguson <wpferguson@gamil.com>.
|
Copyright (C) 2024 Bill Ferguson <wpferguson.com>.
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
* look at the results and choose to delete or not
|
* look at the results and choose to delete or not
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
BUGS, COMMENTS, SUGGESTIONS
|
||||||
Bill Ferguson <wpferguson@gamil.com>
|
Bill Ferguson <wpferguson.com>
|
||||||
|
|
||||||
CHANGES
|
CHANGES
|
||||||
]]
|
]]
|
||||||
@@ -63,8 +63,10 @@ du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that c
|
|||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext.gettext
|
||||||
|
|
||||||
|
dt.gettext.bindtextdomain(MODULE , dt.configuration.config_dir .. "/lua/locale/")
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext(MODULE, msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -80,9 +82,9 @@ script_data.restart = nil -- how to restart the (lib) script after it'
|
|||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
script_data.metadata = {
|
script_data.metadata = {
|
||||||
name = _("db maintenance"),
|
name = "dbmaint",
|
||||||
purpose = _("perform database maintenance"),
|
purpose = _("perform database maintenance"),
|
||||||
author = "Bill Ferguson <wpferguson@gamil.com>",
|
author = "Bill Ferguson <wpferguson.com>",
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/dbmaint/"
|
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/dbmaint/"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,20 +117,6 @@ local namespace = dbmaint
|
|||||||
-- F U N C T I O N S
|
-- F U N C T I O N S
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
-------------------
|
|
||||||
-- helper functions
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
local function set_log_level(level)
|
|
||||||
local old_log_level = log.log_level()
|
|
||||||
log.log_level(level)
|
|
||||||
return old_log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_log_level(level)
|
|
||||||
log.log_level(level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scan_film_rolls()
|
local function scan_film_rolls()
|
||||||
local missing_films = {}
|
local missing_films = {}
|
||||||
|
|
||||||
@@ -142,21 +130,19 @@ local function scan_film_rolls()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function scan_images(film)
|
local function scan_images(film)
|
||||||
local old_log_level = set_log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
local missing_images = {}
|
local missing_images = {}
|
||||||
|
|
||||||
if film then
|
if film then
|
||||||
for i = 1, #film do
|
for i = 1, #film do
|
||||||
local image = film[i]
|
local image = film[i]
|
||||||
log.msg(log.debug, "checking " .. image.filename)
|
log.log_msg(log.debug, "checking " .. image.filename)
|
||||||
if not df.check_if_file_exists(image.path .. PS .. image.filename) then
|
if not df.check_if_file_exists(image.path .. PS .. image.filename) then
|
||||||
log.msg(log.info, image.filename .. " not found")
|
log.log_msg(log.info, image.filename .. " not found")
|
||||||
table.insert(missing_images, image)
|
table.insert(missing_images, image)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
return missing_images
|
return missing_images
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -209,23 +195,21 @@ dbmaint.list_widget = dt.new_widget("text_view"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbmaint.chooser = dt.new_widget("combobox"){
|
dbmaint.chooser = dt.new_widget("combobox"){
|
||||||
label = _("scan for"),
|
label = "scan for",
|
||||||
selected = 1,
|
selected = 1,
|
||||||
_("film rolls"), _("images"),
|
"film rolls", "images",
|
||||||
reset_callback = function(this)
|
reset_callback = function(this)
|
||||||
this.selected = 1
|
this.selected = 1
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
dbmaint.scan_button = dt.new_widget("button"){
|
dbmaint.scan_button = dt.new_widget("button"){
|
||||||
label = _("scan"),
|
label = "scan",
|
||||||
tooltip = _("click to scan for missing film rolls/files"),
|
tooltip = "click to scan for missing film rolls/files",
|
||||||
clicked_callback = function(this)
|
clicked_callback = function(this)
|
||||||
local found = nil
|
local found = nil
|
||||||
local found_text = ""
|
local found_text = ""
|
||||||
local old_log_level = set_log_level(DEFAULT_LOG_LEVEL)
|
if dbmaint.chooser.value == "film rolls" then
|
||||||
log.msg(log.debug, "Button clicked")
|
|
||||||
if dbmaint.chooser.selected == 1 then -- film rolls
|
|
||||||
found = scan_film_rolls()
|
found = scan_film_rolls()
|
||||||
if #found > 0 then
|
if #found > 0 then
|
||||||
for _, film in ipairs(found) do
|
for _, film in ipairs(found) do
|
||||||
@@ -234,7 +218,7 @@ dbmaint.scan_button = dt.new_widget("button"){
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.msg(log.debug, "checking path " .. dt.collection[1].path .. " for missing files")
|
log.log_msg(log.debug, "checking path " .. dt.collection[1].path .. " for missing files")
|
||||||
found = scan_images(dt.collection[1].film)
|
found = scan_images(dt.collection[1].film)
|
||||||
if #found > 0 then
|
if #found > 0 then
|
||||||
for _, image in ipairs(found) do
|
for _, image in ipairs(found) do
|
||||||
@@ -243,14 +227,10 @@ dbmaint.scan_button = dt.new_widget("button"){
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #found > 0 then
|
if #found > 0 then
|
||||||
log.msg(log.debug, "found " .. #found .. " missing items")
|
|
||||||
dbmaint.list_widget.text = found_text
|
dbmaint.list_widget.text = found_text
|
||||||
dbmaint.found = found
|
dbmaint.found = found
|
||||||
dbmaint.remove_button.sensitive = true
|
dbmaint.remove_button.sensitive = true
|
||||||
else
|
|
||||||
log.msg(log.debug, "no missing items found")
|
|
||||||
end
|
end
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end,
|
end,
|
||||||
reset_callback = function(this)
|
reset_callback = function(this)
|
||||||
dbmaint.found = nil
|
dbmaint.found = nil
|
||||||
@@ -258,11 +238,11 @@ dbmaint.scan_button = dt.new_widget("button"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbmaint.remove_button = dt.new_widget("button"){
|
dbmaint.remove_button = dt.new_widget("button"){
|
||||||
label = _("remove"),
|
label = "remove",
|
||||||
tooltip = _("remove missing film rolls/images"),
|
tooltip = "remove missing film rolls/images",
|
||||||
sensitive = false,
|
sensitive = false,
|
||||||
clicked_callback = function(this)
|
clicked_callback = function(this)
|
||||||
if dbmaint.chooser.selected == 1 then -- film rolls
|
if dbmaint.chooser.value == "film rolls" then
|
||||||
remove_missing_film_rolls(dbmaint.found)
|
remove_missing_film_rolls(dbmaint.found)
|
||||||
else
|
else
|
||||||
remove_missing_images(dbmaint.found)
|
remove_missing_images(dbmaint.found)
|
||||||
@@ -278,7 +258,7 @@ dbmaint.remove_button = dt.new_widget("button"){
|
|||||||
|
|
||||||
dbmaint.main_widget = dt.new_widget("box"){
|
dbmaint.main_widget = dt.new_widget("box"){
|
||||||
orientation = "vertical",
|
orientation = "vertical",
|
||||||
dt.new_widget("section_label"){label = _("missing items")},
|
dt.new_widget("section_label"){label = "missing items"},
|
||||||
dbmaint.list_widget,
|
dbmaint.list_widget,
|
||||||
dt.new_widget("label"){label = ""},
|
dt.new_widget("label"){label = ""},
|
||||||
dbmaint.chooser,
|
dbmaint.chooser,
|
||||||
|
|||||||
@@ -67,29 +67,22 @@ if dt.configuration.running_os == 'windows' then os_path_seperator = '\\' end
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", "enfuseAdvanced")
|
du.check_min_api_version("7.0.0", "enfuseAdvanced")
|
||||||
|
|
||||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("enfuse advanced"),
|
|
||||||
purpose = _("focus stack or exposure blend images"),
|
|
||||||
author = "Kevin Ertel",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/enfuseAdvanced"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
local gettext = dt.gettext
|
||||||
|
gettext.bindtextdomain('enfuseAdvanced',dt.configuration.config_dir..'/lua/locale/')
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext('enfuseAdvanced', msgid)
|
||||||
|
end
|
||||||
|
|
||||||
-- INITS --
|
-- INITS --
|
||||||
local AIS = {
|
local AIS = {
|
||||||
name = 'align_image_stack',
|
name = 'align_image_stack',
|
||||||
@@ -267,7 +260,7 @@ local function ExeUpdate(prog_tbl) --updates executable paths and verifies them
|
|||||||
if not prog.bin then
|
if not prog.bin then
|
||||||
prog.install_error = true
|
prog.install_error = true
|
||||||
dt.preferences.write(mod, 'bin_exists', 'bool', false)
|
dt.preferences.write(mod, 'bin_exists', 'bool', false)
|
||||||
dt.print(string.format(_("issue with %s executable"), prog.name))
|
dt.print(_('issue with ')..prog.name.._(' executable'))
|
||||||
else
|
else
|
||||||
prog.bin = CleanSpaces(prog.bin)
|
prog.bin = CleanSpaces(prog.bin)
|
||||||
end
|
end
|
||||||
@@ -288,13 +281,7 @@ local function GetArgsFromPreference(prog_table, prefix) --for each arg in a pro
|
|||||||
local temp = dt.preferences.read(mod, prefix..argument, arg_data.style)
|
local temp = dt.preferences.read(mod, prefix..argument, arg_data.style)
|
||||||
if arg_data.style == 'bool' and temp then
|
if arg_data.style == 'bool' and temp then
|
||||||
prog_table.arg_string = prog_table.arg_string..arg_data.text..' '
|
prog_table.arg_string = prog_table.arg_string..arg_data.text..' '
|
||||||
elseif arg_data.style == 'integer' then
|
elseif arg_data.style == 'integer' or arg_data.style == 'string' then
|
||||||
prog_table.arg_string = prog_table.arg_string..arg_data.text..temp..' '
|
|
||||||
elseif arg_data.style == 'string' then
|
|
||||||
if dt.configuration.running_os == "windows" then
|
|
||||||
--escape % with %% as % means env var on windows and call will fail otherwise
|
|
||||||
temp = string.gsub(temp, "%%", "%%%%")
|
|
||||||
end
|
|
||||||
prog_table.arg_string = prog_table.arg_string..arg_data.text..temp..' '
|
prog_table.arg_string = prog_table.arg_string..arg_data.text..temp..' '
|
||||||
elseif arg_data.style == 'float' then
|
elseif arg_data.style == 'float' then
|
||||||
temp = string.sub(tostring(temp),1,3)
|
temp = string.sub(tostring(temp),1,3)
|
||||||
@@ -351,7 +338,7 @@ local function UpdateENFargs(image_table, prefix) --updates the Enfuse arguments
|
|||||||
end
|
end
|
||||||
ENF.arg_string = ENF.arg_string..' --depth='..GUI.Target.depth.value..' '
|
ENF.arg_string = ENF.arg_string..' --depth='..GUI.Target.depth.value..' '
|
||||||
if GUI.Target.format.value == 'tif' then ENF.arg_string = ENF.arg_string..'--compression='..GUI.Target.compression_level_tif.value..' '
|
if GUI.Target.format.value == 'tif' then ENF.arg_string = ENF.arg_string..'--compression='..GUI.Target.compression_level_tif.value..' '
|
||||||
elseif GUI.Target.format.value == 'jpg' then ENF.arg_string = ENF.arg_string..'--compression='..tostring(math.floor(GUI.Target.compression_level_jpg.value))..' '
|
elseif GUI.Target.format.value == 'jpg' then ENF.arg_string = ENF.arg_string..'--compression='..GUI.Target.compression_level_jpg.value..' '
|
||||||
end
|
end
|
||||||
if not GUI.Target.source_location.value then out_path = GUI.Target.output_directory.value end
|
if not GUI.Target.source_location.value then out_path = GUI.Target.output_directory.value end
|
||||||
out_name = smallest_name..'-'..largest_id
|
out_name = smallest_name..'-'..largest_id
|
||||||
@@ -390,7 +377,7 @@ local function SaveToPreference(preset) --save the present values of enfuse GUI
|
|||||||
dt.preferences.write(mod, preset..argument, arg_data.style, temp)
|
dt.preferences.write(mod, preset..argument, arg_data.style, temp)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
dt.print(string.format(_("saved to %s"), preset))
|
dt.print(_('saved to ')..preset)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function LoadFromPreference(preset) --load values from the specified 'preset' into the GUI elements
|
local function LoadFromPreference(preset) --load values from the specified 'preset' into the GUI elements
|
||||||
@@ -406,7 +393,7 @@ local function LoadFromPreference(preset) --load values from the specified 'pres
|
|||||||
dt.preferences.write(mod, 'active_'..argument, arg_data.style, temp)
|
dt.preferences.write(mod, 'active_'..argument, arg_data.style, temp)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
dt.print(string.format(_("loaded from %s"), preset))
|
dt.print(_('loaded from ')..preset)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function remove_temp_files(images_to_remove) --deletes all files specified by the input string
|
local function remove_temp_files(images_to_remove) --deletes all files specified by the input string
|
||||||
@@ -441,7 +428,7 @@ local function support_format(storage, format) --tells dt we only support TIFF e
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data) --outputs message to user showing script export status
|
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data) --outputs message to user showing script export status
|
||||||
dt.print(string.format(_("export for image fusion %d / %d"), math.floor(number), math.floor(total)))
|
dt.print(_('export for image fusion ')..tostring(math.floor(number))..' / '..tostring(math.floor(total)))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function main(storage, image_table, extra_data)
|
local function main(storage, image_table, extra_data)
|
||||||
@@ -449,7 +436,7 @@ local function main(storage, image_table, extra_data)
|
|||||||
dt.print(_('too few images selected, please select at least 2 images'))
|
dt.print(_('too few images selected, please select at least 2 images'))
|
||||||
return
|
return
|
||||||
elseif extra_data[1] == 2 then
|
elseif extra_data[1] == 2 then
|
||||||
dt.print(_('installation error, please verify binary paths are correct'))
|
dt.print(_('installation error, please verify binary paths are proper'))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local images_to_remove = ''
|
local images_to_remove = ''
|
||||||
@@ -469,8 +456,8 @@ local function main(storage, image_table, extra_data)
|
|||||||
job.valid = false
|
job.valid = false
|
||||||
if resp ~= 0 then
|
if resp ~= 0 then
|
||||||
remove_temp_files(images_to_remove)
|
remove_temp_files(images_to_remove)
|
||||||
dt.print(string.format(_("%s failed"), AIS.name))
|
dt.print_error(AIS.name.._(' failed'))
|
||||||
dt.print_error(AIS.name .. ' failed')
|
dt.print(AIS.name.._(' failed'))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -496,9 +483,8 @@ local function main(storage, image_table, extra_data)
|
|||||||
local resp = dsys.external_command(run_cmd)
|
local resp = dsys.external_command(run_cmd)
|
||||||
if resp ~= 0 then
|
if resp ~= 0 then
|
||||||
remove_temp_files(images_to_remove)
|
remove_temp_files(images_to_remove)
|
||||||
dt.print_error(ENF.name..' failed')
|
dt.print_error(ENF.name.._(' failed'))
|
||||||
dt.print(string.format(_("%s failed"), ENF.name))
|
dt.print(ENF.name.._(' failed'))
|
||||||
job.valid = false
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -743,7 +729,7 @@ GUI.ENF.hard_masks = dt.new_widget('check_button'){
|
|||||||
GUI.ENF.save_masks = dt.new_widget('check_button'){
|
GUI.ENF.save_masks = dt.new_widget('check_button'){
|
||||||
label = _('save masks'),
|
label = _('save masks'),
|
||||||
value = dt.preferences.read(mod, 'active_save_masks', 'bool'),
|
value = dt.preferences.read(mod, 'active_save_masks', 'bool'),
|
||||||
tooltip = _('save the generated weight masks to your home directory,\nenblend saves masks as 8 bit grayscale, \ni.e. single channel images. \nfor accuracy we recommend to choose a lossless format.'),
|
tooltip = _('Save the generated weight masks to your home directory,\nenblend saves masks as 8 bit grayscale, \ni.e. single channel images. \nfor accuracy we recommend to choose a lossless format.'),
|
||||||
clicked_callback = function(self) dt.preferences.write(mod, 'active_save_masks', 'bool', self.value) end,
|
clicked_callback = function(self) dt.preferences.write(mod, 'active_save_masks', 'bool', self.value) end,
|
||||||
reset_callback = function(self) self.value = false end
|
reset_callback = function(self) self.value = false end
|
||||||
}
|
}
|
||||||
@@ -896,7 +882,7 @@ GUI.Target.output_directory = dt.new_widget('file_chooser_button'){
|
|||||||
GUI.Target.source_location = dt.new_widget('check_button'){
|
GUI.Target.source_location = dt.new_widget('check_button'){
|
||||||
label = _('save to source image location'),
|
label = _('save to source image location'),
|
||||||
value = dt.preferences.read(mod, 'active_source_location', 'bool'),
|
value = dt.preferences.read(mod, 'active_source_location', 'bool'),
|
||||||
tooltip = _('if checked ignores the location above and saves output image(s) to the same location as the source images.'),
|
tooltip = _('If checked ignores the location above and saves output image(s) to the same location as the source images.'),
|
||||||
clicked_callback = function(self) dt.preferences.write(mod, 'active_source_location', 'bool', self.value) end,
|
clicked_callback = function(self) dt.preferences.write(mod, 'active_source_location', 'bool', self.value) end,
|
||||||
reset_callback = function(self) self.value = true end
|
reset_callback = function(self) self.value = true end
|
||||||
}
|
}
|
||||||
@@ -930,8 +916,8 @@ GUI.Target.auto_import = dt.new_widget('check_button'){
|
|||||||
}
|
}
|
||||||
temp = dt.preferences.read(mod, 'active_apply_style_ind', 'integer')
|
temp = dt.preferences.read(mod, 'active_apply_style_ind', 'integer')
|
||||||
GUI.Target.apply_style = dt.new_widget('combobox'){
|
GUI.Target.apply_style = dt.new_widget('combobox'){
|
||||||
label = _('apply style on Import'),
|
label = _('Apply Style on Import'),
|
||||||
tooltip = _('apply selected style on auto-import to newly created blended image'),
|
tooltip = _('Apply selected style on auto-import to newly created blended image'),
|
||||||
selected = 1,
|
selected = 1,
|
||||||
'none',
|
'none',
|
||||||
changed_callback = function(self)
|
changed_callback = function(self)
|
||||||
@@ -952,16 +938,16 @@ GUI.Target.apply_style.selected = temp
|
|||||||
GUI.Target.copy_tags = dt.new_widget('check_button'){
|
GUI.Target.copy_tags = dt.new_widget('check_button'){
|
||||||
label = _('copy tags'),
|
label = _('copy tags'),
|
||||||
value = dt.preferences.read(mod, 'active_copy_tags', 'bool'),
|
value = dt.preferences.read(mod, 'active_copy_tags', 'bool'),
|
||||||
tooltip = _('copy tags from first image.'),
|
tooltip = _('Copy tags from first image.'),
|
||||||
clicked_callback = function(self) dt.preferences.write(mod, 'active_copy_tags', 'bool', self.value) end,
|
clicked_callback = function(self) dt.preferences.write(mod, 'active_copy_tags', 'bool', self.value) end,
|
||||||
reset_callback = function(self) self.value = true end
|
reset_callback = function(self) self.value = true end
|
||||||
}
|
}
|
||||||
temp = dt.preferences.read(mod, 'active_add_tags', 'string')
|
temp = dt.preferences.read(mod, 'active_add_tags', 'string')
|
||||||
if temp == '' then temp = nil end
|
if temp == '' then temp = nil end
|
||||||
GUI.Target.add_tags = dt.new_widget('entry'){
|
GUI.Target.add_tags = dt.new_widget('entry'){
|
||||||
tooltip = _('additional tags to be added on import, seperate with commas, all spaces will be removed'),
|
tooltip = _('Additional tags to be added on import. Seperate with commas, all spaces will be removed'),
|
||||||
text = temp,
|
text = temp,
|
||||||
placeholder = _('enter tags, separated by commas'),
|
placeholder = 'Enter tags, seperated by commas',
|
||||||
editable = true
|
editable = true
|
||||||
}
|
}
|
||||||
temp = dt.preferences.read(mod, 'active_current_preset_ind', 'integer')
|
temp = dt.preferences.read(mod, 'active_current_preset_ind', 'integer')
|
||||||
@@ -1028,7 +1014,7 @@ GUI.Presets.variants_type = dt.new_widget('combobox'){
|
|||||||
GUI.Presets.variants_type.sensitive = GUI.Presets.variants.value
|
GUI.Presets.variants_type.sensitive = GUI.Presets.variants.value
|
||||||
temp = df.get_executable_path_preference(AIS.name)
|
temp = df.get_executable_path_preference(AIS.name)
|
||||||
GUI.exes.align_image_stack = dt.new_widget('file_chooser_button'){
|
GUI.exes.align_image_stack = dt.new_widget('file_chooser_button'){
|
||||||
title = 'align_image_stack ' .. _('binary path'),
|
title = 'AIS binary path',
|
||||||
value = temp,
|
value = temp,
|
||||||
tooltip = temp,
|
tooltip = temp,
|
||||||
is_directory = false,
|
is_directory = false,
|
||||||
@@ -1036,7 +1022,7 @@ GUI.exes.align_image_stack = dt.new_widget('file_chooser_button'){
|
|||||||
}
|
}
|
||||||
temp = df.get_executable_path_preference(ENF.name)
|
temp = df.get_executable_path_preference(ENF.name)
|
||||||
GUI.exes.enfuse = dt.new_widget('file_chooser_button'){
|
GUI.exes.enfuse = dt.new_widget('file_chooser_button'){
|
||||||
title = 'enfuse ' .. _('binary path'),
|
title = 'enfuse binary path',
|
||||||
value = temp,
|
value = temp,
|
||||||
tooltip = temp,
|
tooltip = temp,
|
||||||
is_directory = false,
|
is_directory = false,
|
||||||
@@ -1044,7 +1030,7 @@ GUI.exes.enfuse = dt.new_widget('file_chooser_button'){
|
|||||||
}
|
}
|
||||||
temp = df.get_executable_path_preference(EXF.name)
|
temp = df.get_executable_path_preference(EXF.name)
|
||||||
GUI.exes.exiftool = dt.new_widget('file_chooser_button'){
|
GUI.exes.exiftool = dt.new_widget('file_chooser_button'){
|
||||||
title = 'exiftool ' .. _('binary path'),
|
title = 'Exiftool binary path',
|
||||||
value = temp,
|
value = temp,
|
||||||
tooltip = temp,
|
tooltip = temp,
|
||||||
is_directory = false,
|
is_directory = false,
|
||||||
@@ -1172,4 +1158,4 @@ end
|
|||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
@@ -33,28 +33,23 @@ local ds = require("lib/dtutils.system")
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", "exportLUT")
|
du.check_min_api_version("7.0.0", "exportLUT")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("export LUT"),
|
|
||||||
purpose = _("export a style as a LUT"),
|
|
||||||
author = "Noah Clarke",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/exportLUT"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
gettext.bindtextdomain("exportLUT",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("exportLUT", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
du.check_min_api_version("5.0.0", "exportLUT")
|
du.check_min_api_version("5.0.0", "exportLUT")
|
||||||
|
|
||||||
local eL = {}
|
local eL = {}
|
||||||
@@ -69,13 +64,13 @@ local mkdir_command = 'mkdir -p '
|
|||||||
if dt.configuration.running_os == 'windows' then mkdir_command = 'mkdir ' end
|
if dt.configuration.running_os == 'windows' then mkdir_command = 'mkdir ' end
|
||||||
|
|
||||||
local file_chooser_button = dt.new_widget("file_chooser_button"){
|
local file_chooser_button = dt.new_widget("file_chooser_button"){
|
||||||
title = _("choose the identity file"),
|
title = _("Identity_file_chooser"),
|
||||||
value = "",
|
value = "",
|
||||||
is_directory = false
|
is_directory = false
|
||||||
}
|
}
|
||||||
|
|
||||||
local export_chooser_button = dt.new_widget("file_chooser_button"){
|
local export_chooser_button = dt.new_widget("file_chooser_button"){
|
||||||
title = _("choose the export location"),
|
title = _("Export_location_chooser"),
|
||||||
value = "",
|
value = "",
|
||||||
is_directory = true
|
is_directory = true
|
||||||
}
|
}
|
||||||
@@ -111,9 +106,9 @@ end
|
|||||||
local function export_luts()
|
local function export_luts()
|
||||||
local identity = dt.database.import(file_chooser_button.value)
|
local identity = dt.database.import(file_chooser_button.value)
|
||||||
if(type(identity) ~= "userdata") then
|
if(type(identity) ~= "userdata") then
|
||||||
dt.print(_("invalid identity lut file"))
|
dt.print(_("Invalid identity lut file"))
|
||||||
else
|
else
|
||||||
local job = dt.gui.create_job(_('exporting styles as haldCLUTs'), true, end_job)
|
local job = dt.gui.create_job(_('Exporting styles as haldCLUTs'), true, end_job)
|
||||||
|
|
||||||
local size = 1
|
local size = 1
|
||||||
|
|
||||||
@@ -132,9 +127,9 @@ local function export_luts()
|
|||||||
io_lut:write_image(identity, output_path(style.name, job))
|
io_lut:write_image(identity, output_path(style.name, job))
|
||||||
count = count + 1
|
count = count + 1
|
||||||
job.percent = count / size
|
job.percent = count / size
|
||||||
dt.print(string.format(_("exported: %s"), output_path(style.name, job)))
|
dt.print(_("Exported: ") .. output_path(style.name, job))
|
||||||
end
|
end
|
||||||
dt.print(_("done exporting haldCLUTs"))
|
dt.print(_("Done exporting haldCLUTs"))
|
||||||
job.valid = false
|
job.valid = false
|
||||||
identity:reset()
|
identity:reset()
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -75,24 +75,10 @@ local dtsys = require "lib/dtutils.system"
|
|||||||
local MODULE_NAME = "ext_editor"
|
local MODULE_NAME = "ext_editor"
|
||||||
du.check_min_api_version("7.0.0", MODULE_NAME)
|
du.check_min_api_version("7.0.0", MODULE_NAME)
|
||||||
|
|
||||||
-- translation
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("external editors"),
|
|
||||||
purpose = _("edit images with external editors"),
|
|
||||||
author = "Marco Carrarini, marco.carrarini@gmail.com",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/ext_editor"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -108,6 +94,13 @@ ee.event_registered = false
|
|||||||
ee.widgets = {}
|
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
|
-- maximum number of external programs, can be increased to necessity
|
||||||
local MAX_EDITORS = 9
|
local MAX_EDITORS = 9
|
||||||
|
|
||||||
@@ -163,7 +156,7 @@ local function UpdateProgramList(combobox, button_edit, button_edit_copy, update
|
|||||||
button_edit.sensitive = active
|
button_edit.sensitive = active
|
||||||
button_edit_copy.sensitive = active
|
button_edit_copy.sensitive = active
|
||||||
|
|
||||||
if update_button_pressed then dt.print(string.format(_("%d editors configured"), n_entries)) end
|
if update_button_pressed then dt.print(n_entries.._(" editors configured")) end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -230,17 +223,17 @@ local function OpenWith(images, choice, copy)
|
|||||||
-- physical copy, check result, return if error
|
-- physical copy, check result, return if error
|
||||||
local copy_success = df.file_copy(name, new_name)
|
local copy_success = df.file_copy(name, new_name)
|
||||||
if not copy_success then
|
if not copy_success then
|
||||||
dt.print(string.format(_("error copying file %s"), name))
|
dt.print(_("error copying file ")..name)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- launch the external editor, check result, return if error
|
-- launch the external editor, check result, return if error
|
||||||
local run_cmd = bin.." "..df.sanitize_filename(new_name)
|
local run_cmd = bin.." "..df.sanitize_filename(new_name)
|
||||||
dt.print(string.format(_("launching %s..."), friendly_name))
|
dt.print(_("launching ")..friendly_name.."...")
|
||||||
local result = dtsys.external_command(run_cmd)
|
local result = dtsys.external_command(run_cmd)
|
||||||
if result ~= 0 then
|
if result ~= 0 then
|
||||||
dt.print(string.format(_("error launching %s"), friendly_name))
|
dt.print(_("error launching ")..friendly_name)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -333,7 +326,7 @@ local function export2collection(storage, image_table, extra_data)
|
|||||||
-- move image to collection folder, check result, return if error
|
-- move image to collection folder, check result, return if error
|
||||||
move_success = df.file_move(temp_name, new_name)
|
move_success = df.file_move(temp_name, new_name)
|
||||||
if not move_success then
|
if not move_success then
|
||||||
dt.print(string.format(_("error moving file %s"), temp_name))
|
dt.print(_("error moving file ")..temp_name)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -385,7 +378,7 @@ end
|
|||||||
local function restart()
|
local function restart()
|
||||||
for i = 1, MAX_EDITORS do
|
for i = 1, MAX_EDITORS do
|
||||||
dt.register_event(MODULE_NAME .. i, "shortcut",
|
dt.register_event(MODULE_NAME .. i, "shortcut",
|
||||||
program_shortcut, string.format(_("edit with program %02d"), i))
|
program_shortcut, _("edit with program ")..string.format("%02d", i))
|
||||||
end
|
end
|
||||||
dt.register_storage("exp2coll", _("collection"), nil, export2collection)
|
dt.register_storage("exp2coll", _("collection"), nil, export2collection)
|
||||||
dt.gui.libs[MODULE_NAME].visible = true
|
dt.gui.libs[MODULE_NAME].visible = true
|
||||||
@@ -483,11 +476,11 @@ dt.register_storage("exp2coll", _("collection"), nil, export2collection)
|
|||||||
-- register the new preferences -----------------------------------------------
|
-- register the new preferences -----------------------------------------------
|
||||||
for i = MAX_EDITORS, 1, -1 do
|
for i = MAX_EDITORS, 1, -1 do
|
||||||
dt.preferences.register(MODULE_NAME, "program_path_"..i, "file",
|
dt.preferences.register(MODULE_NAME, "program_path_"..i, "file",
|
||||||
string.format(_("executable for external editor %d"), i),
|
_("executable for external editor ")..i,
|
||||||
_("select executable for external editor") , _("(none)"))
|
_("select executable for external editor") , _("(None)"))
|
||||||
|
|
||||||
dt.preferences.register(MODULE_NAME, "program_name_"..i, "string",
|
dt.preferences.register(MODULE_NAME, "program_name_"..i, "string",
|
||||||
string.format(_("name of external editor %d"), i),
|
_("name of external editor ")..i,
|
||||||
_("friendly name of external editor"), "")
|
_("friendly name of external editor"), "")
|
||||||
end
|
end
|
||||||
dt.preferences.register(MODULE_NAME, "show_in_darkrooom", "bool",
|
dt.preferences.register(MODULE_NAME, "show_in_darkrooom", "bool",
|
||||||
@@ -498,7 +491,7 @@ dt.preferences.register(MODULE_NAME, "show_in_darkrooom", "bool",
|
|||||||
-- register the new shortcuts -------------------------------------------------
|
-- register the new shortcuts -------------------------------------------------
|
||||||
for i = 1, MAX_EDITORS do
|
for i = 1, MAX_EDITORS do
|
||||||
dt.register_event(MODULE_NAME .. i, "shortcut",
|
dt.register_event(MODULE_NAME .. i, "shortcut",
|
||||||
program_shortcut, string.format(_("edit with program %02d"), i))
|
program_shortcut, _("edit with program ")..string.format("%02d", i))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ local dt = require "darktable"
|
|||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dtsys = require "lib/dtutils.system"
|
local dtsys = require "lib/dtutils.system"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
-- constants
|
-- constants
|
||||||
|
|
||||||
@@ -54,21 +54,10 @@ local OUTPUT = dt.configuration.tmp_dir .. PS .. "facerecognition.txt"
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE)
|
du.check_min_api_version("7.0.0", MODULE)
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("face recognition"),
|
|
||||||
purpose = _("use facial recognition to tag images"),
|
|
||||||
author = "Sebastian Witt",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/face_recognition"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -80,6 +69,13 @@ local fc = {}
|
|||||||
fc.module_installed = false
|
fc.module_installed = false
|
||||||
fc.event_registered = false
|
fc.event_registered = false
|
||||||
|
|
||||||
|
-- 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 function build_image_table(images)
|
||||||
local image_table = {}
|
local image_table = {}
|
||||||
local file_extension = ""
|
local file_extension = ""
|
||||||
@@ -139,7 +135,7 @@ local function do_export(img_tbl, images)
|
|||||||
job.percent = 0.0
|
job.percent = 0.0
|
||||||
for export,img in pairs(img_tbl) do
|
for export,img in pairs(img_tbl) do
|
||||||
exp_cnt = exp_cnt + 1
|
exp_cnt = exp_cnt + 1
|
||||||
dt.print(string.format(_("exporting image %i of %i images"), exp_cnt, images))
|
dt.print(string.format(_("Exporting image %i of %i images"), exp_cnt, images))
|
||||||
exporter:write_image(img, export, upsize)
|
exporter:write_image(img, export, upsize)
|
||||||
job.percent = job.percent + percent_step
|
job.percent = job.percent + percent_step
|
||||||
end
|
end
|
||||||
@@ -209,7 +205,7 @@ local function face_recognition ()
|
|||||||
local bin_path = df.check_if_bin_exists("face_recognition")
|
local bin_path = df.check_if_bin_exists("face_recognition")
|
||||||
|
|
||||||
if not bin_path then
|
if not bin_path then
|
||||||
dt.print(_("face recognition not found"))
|
dt.print(_("Face recognition not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -261,7 +257,7 @@ local function face_recognition ()
|
|||||||
local command = bin_path .. " --cpus " .. nrCores .. " --tolerance " .. tolerance .. " " .. knownPath .. " " .. path .. " > " .. OUTPUT
|
local command = bin_path .. " --cpus " .. nrCores .. " --tolerance " .. tolerance .. " " .. knownPath .. " " .. path .. " > " .. OUTPUT
|
||||||
os.setlocale()
|
os.setlocale()
|
||||||
dt.print_log("Face recognition: Running command: " .. command)
|
dt.print_log("Face recognition: Running command: " .. command)
|
||||||
dt.print(_("starting face recognition..."))
|
dt.print(_("Starting face recognition..."))
|
||||||
|
|
||||||
dtsys.external_command(command)
|
dtsys.external_command(command)
|
||||||
|
|
||||||
@@ -269,9 +265,9 @@ local function face_recognition ()
|
|||||||
local f = io.open(OUTPUT, "rb")
|
local f = io.open(OUTPUT, "rb")
|
||||||
|
|
||||||
if not f then
|
if not f then
|
||||||
dt.print(_("face recognition failed"))
|
dt.print(_("Face recognition failed"))
|
||||||
else
|
else
|
||||||
dt.print(_("face recognition finished"))
|
dt.print(_("Face recognition finished"))
|
||||||
f:close ()
|
f:close ()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -403,7 +399,7 @@ fc.category_tags = dt.new_widget("entry"){
|
|||||||
|
|
||||||
fc.tolerance = dt.new_widget("slider"){
|
fc.tolerance = dt.new_widget("slider"){
|
||||||
label = _("tolerance"),
|
label = _("tolerance"),
|
||||||
tooltip = _("detection tolerance - 0.6 default - lower if too many faces detected"),
|
tooltip = ("detection tolerance - 0.6 default - lower if too many faces detected"),
|
||||||
soft_min = 0.0,
|
soft_min = 0.0,
|
||||||
hard_min = 0.0,
|
hard_min = 0.0,
|
||||||
soft_max = 1.0,
|
soft_max = 1.0,
|
||||||
@@ -486,12 +482,12 @@ table.insert(widgets, fc.num_cores)
|
|||||||
table.insert(widgets, fc.export_format)
|
table.insert(widgets, fc.export_format)
|
||||||
table.insert(widgets, dt.new_widget("box"){
|
table.insert(widgets, dt.new_widget("box"){
|
||||||
orientation = "horizontal",
|
orientation = "horizontal",
|
||||||
dt.new_widget("label"){ label = _("width")},
|
dt.new_widget("label"){ label = _("width ")},
|
||||||
fc.width,
|
fc.width,
|
||||||
})
|
})
|
||||||
table.insert(widgets, dt.new_widget("box"){
|
table.insert(widgets, dt.new_widget("box"){
|
||||||
orientation = "horizontal",
|
orientation = "horizontal",
|
||||||
dt.new_widget("label"){ label = _("height")},
|
dt.new_widget("label"){ label = _("height ")},
|
||||||
fc.height,
|
fc.height,
|
||||||
})
|
})
|
||||||
table.insert(widgets, fc.execute)
|
table.insert(widgets, fc.execute)
|
||||||
|
|||||||
@@ -60,26 +60,13 @@ cameras may behave in other ways.
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dtsys = require "lib/dtutils.system"
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "fujifilm_dynamic_range")
|
du.check_min_api_version("7.0.0", "fujifilm_dynamic_range")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("fujifilm dynamic range"),
|
|
||||||
purpose = _("compensate for Fujifilm raw files made using \"dynamic range\""),
|
|
||||||
author = "Dan Torop <dant@pnym.net>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/fujifilm_dynamic_range"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -124,17 +111,6 @@ local function detect_dynamic_range(event, image)
|
|||||||
-- note that scene-referred workflow exposure preset also pushes exposure up by 0.5 EV
|
-- 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)
|
image.exif_exposure_bias = image.exif_exposure_bias + tonumber(raf_result)
|
||||||
dt.print_log("[fujifilm_dynamic_range] raw exposure bias " .. tostring(raf_result))
|
dt.print_log("[fujifilm_dynamic_range] raw exposure bias " .. tostring(raf_result))
|
||||||
-- handle any duplicates
|
|
||||||
if #image:get_group_members() > 1 then
|
|
||||||
local basename = df.get_basename(image.filename)
|
|
||||||
local grouped_images = image:get_group_members()
|
|
||||||
for _, img in ipairs(grouped_images) do
|
|
||||||
if string.match(img.filename, basename) and img.duplicate_index > 0 then
|
|
||||||
-- its a duplicate
|
|
||||||
img.exif_exposure_bias = img.exif_exposure_bias + tonumber(raf_result)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function destroy()
|
local function destroy()
|
||||||
|
|||||||
@@ -26,61 +26,52 @@ Dependencies:
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dtsys = require "lib/dtutils.system"
|
local gettext = dt.gettext
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "fujifilm_ratings")
|
du.check_min_api_version("7.0.0", "fujifilm_ratings")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("fujifilm ratings"),
|
|
||||||
purpose = _("import Fujifilm in-camera ratings"),
|
|
||||||
author = "Ben Mendis <ben.mendis@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/fujifilm_ratings"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
gettext.bindtextdomain("fujifilm_ratings", dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("fujifilm_ratings", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local function detect_rating(event, image)
|
local function detect_rating(event, image)
|
||||||
if not string.match(image.filename, "%.RAF$") and not string.match(image.filename, "%.raf$") then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not df.check_if_bin_exists("exiftool") then
|
if not df.check_if_bin_exists("exiftool") then
|
||||||
dt.print_error("exiftool not found")
|
dt.print_error(_("exiftool not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local RAF_filename = df.sanitize_filename(tostring(image))
|
local RAF_filename = df.sanitize_filename(tostring(image))
|
||||||
local JPEG_filename = string.gsub(RAF_filename, "%.RAF$", ".JPG")
|
local JPEG_filename = string.gsub(RAF_filename, "%.RAF$", ".JPG")
|
||||||
local command = "exiftool -Rating " .. JPEG_filename
|
local command = "exiftool -Rating " .. JPEG_filename
|
||||||
dt.print_log(command)
|
dt.print_error(command)
|
||||||
local output = io.popen(command)
|
local output = io.popen(command)
|
||||||
local jpeg_result = output:read("*all")
|
local jpeg_result = output:read("*all")
|
||||||
output:close()
|
output:close()
|
||||||
if string.len(jpeg_result) > 0 then
|
if string.len(jpeg_result) > 0 then
|
||||||
jpeg_result = string.gsub(jpeg_result, "^Rating.*(%d)", "%1")
|
jpeg_result = string.gsub(jpeg_result, "^Rating.*(%d)", "%1")
|
||||||
image.rating = tonumber(jpeg_result)
|
image.rating = tonumber(jpeg_result)
|
||||||
dt.print_log("using JPEG rating: " .. jpeg_result)
|
dt.print_error(_("Using JPEG Rating: ") .. tostring(jpeg_result))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
command = "exiftool -Rating " .. RAF_filename
|
command = "exiftool -Rating " .. RAF_filename
|
||||||
dt.print_log(command)
|
dt.print_error(command)
|
||||||
output = io.popen(command)
|
output = io.popen(command)
|
||||||
local raf_result = output:read("*all")
|
local raf_result = output:read("*all")
|
||||||
output:close()
|
output:close()
|
||||||
if string.len(raf_result) > 0 then
|
if string.len(raf_result) > 0 then
|
||||||
raf_result = string.gsub(raf_result, "^Rating.*(%d)", "%1")
|
raf_result = string.gsub(raf_result, "^Rating.*(%d)", "%1")
|
||||||
image.rating = tonumber(raf_result)
|
image.rating = tonumber(raf_result)
|
||||||
dt.print_log("using RAF rating: " .. raf_result)
|
dt.print_error(_("Using RAF Rating: ") .. tostring(raf_result))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -35,31 +35,26 @@ USAGE
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dtsys = require "lib/dtutils.system"
|
local gettext = dt.gettext
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "geoJSON_export")
|
du.check_min_api_version("7.0.0", "geoJSON_export")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("geoJSON export"),
|
|
||||||
purpose = _("export a geoJSON file from geo data"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/geoJSON_export"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("geoJSON_export",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("geoJSON_export", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
-- Sort a table
|
-- Sort a table
|
||||||
local function spairs(_table, order) -- Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
|
local function spairs(_table, order) -- Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
|
||||||
-- collect the keys
|
-- collect the keys
|
||||||
@@ -85,24 +80,24 @@ local function spairs(_table, order) -- Code copied from http://stackoverflow.co
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
||||||
dt.print(string.format(_("export image %i/%i"), number, total))
|
dt.print(string.format(_("Export Image %i/%i"), number, total))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function create_geoJSON_file(storage, image_table, extra_data)
|
local function create_geoJSON_file(storage, image_table, extra_data)
|
||||||
if not df.check_if_bin_exists("mkdir") then
|
if not df.check_if_bin_exists("mkdir") then
|
||||||
dt.print_error("mkdir not found")
|
dt.print_error(_("mkdir not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not df.check_if_bin_exists("convert") then
|
if not df.check_if_bin_exists("convert") then
|
||||||
dt.print_error("convert not found")
|
dt.print_error(_("convert not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not df.check_if_bin_exists("xdg-open") then
|
if not df.check_if_bin_exists("xdg-open") then
|
||||||
dt.print_error("xdg-open not found")
|
dt.print_error(_("xdg-open not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not df.check_if_bin_exists("xdg-user-dir") then
|
if not df.check_if_bin_exists("xdg-user-dir") then
|
||||||
dt.print_error("xdg-user-dir not found")
|
dt.print_error(_("xdg-user-dir not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -293,7 +288,7 @@ local function create_geoJSON_file(storage, image_table, extra_data)
|
|||||||
file:write(geoJSON_file)
|
file:write(geoJSON_file)
|
||||||
file:close()
|
file:close()
|
||||||
|
|
||||||
dt.print(string.format(_("%s file created in %s"), "geoJSON", exportDirectory))
|
dt.print("geoJSON file created in "..exportDirectory)
|
||||||
|
|
||||||
-- Open the file with the standard programm
|
-- Open the file with the standard programm
|
||||||
if ( dt.preferences.read("geoJSON_export","OpengeoJSONFile","bool") == true ) then
|
if ( dt.preferences.read("geoJSON_export","OpengeoJSONFile","bool") == true ) then
|
||||||
@@ -313,20 +308,20 @@ end
|
|||||||
dt.preferences.register("geoJSON_export",
|
dt.preferences.register("geoJSON_export",
|
||||||
"CreateMapBoxHTMLFile",
|
"CreateMapBoxHTMLFile",
|
||||||
"bool",
|
"bool",
|
||||||
"geoJSON export: ".._("Create an additional HTML file"),
|
_("geoJSON export: Create an additional HTML file"),
|
||||||
_("creates an HTML file that loads the geoJSON file. (needs a MapBox key"),
|
_("Creates a HTML file, that loads the geoJASON file. (Needs a MapBox key"),
|
||||||
false )
|
false )
|
||||||
dt.preferences.register("geoJSON_export",
|
dt.preferences.register("geoJSON_export",
|
||||||
"mapBoxKey",
|
"mapBoxKey",
|
||||||
"string",
|
"string",
|
||||||
"geoJSON export: MapBox " .. _("key"),
|
_("geoJSON export: MapBox Key"),
|
||||||
"https://www.mapbox.com/studio/account/tokens",
|
_("https://www.mapbox.com/studio/account/tokens"),
|
||||||
'' )
|
'' )
|
||||||
dt.preferences.register("geoJSON_export",
|
dt.preferences.register("geoJSON_export",
|
||||||
"OpengeoJSONFile",
|
"OpengeoJSONFile",
|
||||||
"bool",
|
"bool",
|
||||||
"geoJSON export: " .. _("open geoJSON file after export"),
|
_("geoJSON export: Open geoJSON file after export"),
|
||||||
_("opens the geoJSON file after the export with the standard program for geoJSON files"),
|
_("Opens the geoJSON file after the export with the standard program for geoJSON files"),
|
||||||
false )
|
false )
|
||||||
|
|
||||||
local handle = io.popen("xdg-user-dir DESKTOP")
|
local handle = io.popen("xdg-user-dir DESKTOP")
|
||||||
@@ -338,8 +333,8 @@ end
|
|||||||
dt.preferences.register("geoJSON_export",
|
dt.preferences.register("geoJSON_export",
|
||||||
"ExportDirectory",
|
"ExportDirectory",
|
||||||
"directory",
|
"directory",
|
||||||
_("geoJSON export: export directory"),
|
_("geoJSON export: Export directory"),
|
||||||
_("a directory that will be used to export the geoJSON files"),
|
_("A directory that will be used to export the geoJSON files"),
|
||||||
result )
|
result )
|
||||||
|
|
||||||
-- Register
|
-- Register
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
--[[
|
--[[
|
||||||
This file is part of darktable,
|
This file is part of darktable,
|
||||||
copyright (c) 2016 Tobias Jakobs
|
copyright (c) 2016 Tobias Jakobs
|
||||||
copyright (c) 2025 Balázs Dura-Kovács
|
|
||||||
|
|
||||||
darktable is free software: you can redistribute it and/or modify
|
darktable is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -29,31 +28,26 @@ require "geoToolbox"
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dtsys = require "lib/dtutils.system"
|
local gettext = dt.gettext
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "geoToolbox")
|
du.check_min_api_version("7.0.0", "geoToolbox")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("geo toolbox"),
|
|
||||||
purpose = _("geodata tools"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/geoToolbox"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("geoToolbox",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("geoToolbox", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local gT = {}
|
local gT = {}
|
||||||
gT.module_installed = false
|
gT.module_installed = false
|
||||||
@@ -62,35 +56,23 @@ gT.event_registered = false
|
|||||||
|
|
||||||
-- <GUI>
|
-- <GUI>
|
||||||
local labelDistance = dt.new_widget("label")
|
local labelDistance = dt.new_widget("label")
|
||||||
labelDistance.label = _("distance:")
|
labelDistance.label = _("Distance:")
|
||||||
|
|
||||||
local checkbox_copy_gps_lat = dt.new_widget("check_button")
|
local label_copy_gps_lat = dt.new_widget("check_button")
|
||||||
{
|
{
|
||||||
|
label = _("latitude:"),
|
||||||
value = true
|
value = true
|
||||||
}
|
}
|
||||||
local entry_gps_lat = dt.new_widget("entry")
|
local label_copy_gps_lon = dt.new_widget("check_button")
|
||||||
{
|
|
||||||
tooltip = _("latitude (editable)"),
|
|
||||||
placeholder = _("latitude")
|
|
||||||
}
|
|
||||||
local checkbox_copy_gps_lon = dt.new_widget("check_button")
|
|
||||||
{
|
{
|
||||||
|
label = _("longitude:"),
|
||||||
value = true
|
value = true
|
||||||
}
|
}
|
||||||
local entry_gps_lon = dt.new_widget("entry")
|
local label_copy_gps_ele = dt.new_widget("check_button")
|
||||||
{
|
|
||||||
tooltip = _("longitude (editable)"),
|
|
||||||
placeholder = _("longitude")
|
|
||||||
}
|
|
||||||
local checkbox_copy_gps_ele = dt.new_widget("check_button")
|
|
||||||
{
|
{
|
||||||
|
label = _("elevation:"),
|
||||||
value = true
|
value = true
|
||||||
}
|
}
|
||||||
local entry_gps_ele = dt.new_widget("entry")
|
|
||||||
{
|
|
||||||
tooltip = _("elevation (editable)"),
|
|
||||||
placeholder = _("elevation")
|
|
||||||
}
|
|
||||||
-- </GUI>
|
-- </GUI>
|
||||||
|
|
||||||
local function select_with_gps()
|
local function select_with_gps()
|
||||||
@@ -180,7 +162,7 @@ local function get_first_coordinate()
|
|||||||
first_elevation = ''
|
first_elevation = ''
|
||||||
first_image_date = 0
|
first_image_date = 0
|
||||||
|
|
||||||
for jj,image in ipairs(sel_images) do
|
for _,image in ipairs(sel_images) do
|
||||||
if not image then
|
if not image then
|
||||||
first_have_data = false
|
first_have_data = false
|
||||||
else
|
else
|
||||||
@@ -219,7 +201,7 @@ local function get_second_coordinate()
|
|||||||
second_elevation = ''
|
second_elevation = ''
|
||||||
second_image_date = 0
|
second_image_date = 0
|
||||||
|
|
||||||
for jj,image in ipairs(sel_images) do
|
for _,image in ipairs(sel_images) do
|
||||||
if not image then
|
if not image then
|
||||||
second_have_data = false
|
second_have_data = false
|
||||||
else
|
else
|
||||||
@@ -255,7 +237,7 @@ local calc_in_between_slider = dt.new_widget("slider")
|
|||||||
--ToDo: this needs more love
|
--ToDo: this needs more love
|
||||||
local function calc_in_between()
|
local function calc_in_between()
|
||||||
local sel_images = dt.gui.action_images
|
local sel_images = dt.gui.action_images
|
||||||
for jj,image in ipairs(sel_images) do
|
for _,image in ipairs(sel_images) do
|
||||||
if image then
|
if image then
|
||||||
image_date = make_time_stamp(image.exif_datetime_taken)
|
image_date = make_time_stamp(image.exif_datetime_taken)
|
||||||
if (first_have_data and second_have_data) then
|
if (first_have_data and second_have_data) then
|
||||||
@@ -302,25 +284,25 @@ local function copy_gps()
|
|||||||
copy_gps_longitude = ''
|
copy_gps_longitude = ''
|
||||||
copy_gps_elevation = ''
|
copy_gps_elevation = ''
|
||||||
|
|
||||||
for jj,image in ipairs(sel_images) do
|
for _,image in ipairs(sel_images) do
|
||||||
if not image then
|
if not image then
|
||||||
copy_gps_have_data = false
|
copy_gps_have_data = false
|
||||||
else
|
else
|
||||||
copy_gps_have_data = true
|
copy_gps_have_data = true
|
||||||
if (image.latitude and checkbox_copy_gps_lat.value) then
|
if (image.latitude and label_copy_gps_lat.value) then
|
||||||
copy_gps_latitude = image.latitude
|
copy_gps_latitude = image.latitude
|
||||||
end
|
end
|
||||||
if (image.longitude and checkbox_copy_gps_lon.value) then
|
if (image.longitude and label_copy_gps_lon.value) then
|
||||||
copy_gps_longitude = image.longitude
|
copy_gps_longitude = image.longitude
|
||||||
end
|
end
|
||||||
if (image.elevation and checkbox_copy_gps_ele.value) then
|
if (image.elevation and label_copy_gps_ele.value) then
|
||||||
copy_gps_elevation = image.elevation
|
copy_gps_elevation = image.elevation
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
entry_gps_lat.text = copy_gps_latitude
|
label_copy_gps_lat.label = _("latitude: ") .. copy_gps_latitude
|
||||||
entry_gps_lon.text = copy_gps_longitude
|
label_copy_gps_lon.label = _("longitude: ") ..copy_gps_longitude
|
||||||
entry_gps_ele.text = copy_gps_elevation
|
label_copy_gps_ele.label = _("elevation: ") .. copy_gps_elevation
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -329,15 +311,15 @@ end
|
|||||||
local function paste_gps(image)
|
local function paste_gps(image)
|
||||||
local sel_images = dt.gui.action_images
|
local sel_images = dt.gui.action_images
|
||||||
|
|
||||||
for jj,image in ipairs(sel_images) do
|
for _,image in ipairs(sel_images) do
|
||||||
if (checkbox_copy_gps_lat.value) then
|
if (label_copy_gps_lat.value) then
|
||||||
image.latitude = entry_gps_lat.text
|
image.latitude = copy_gps_latitude
|
||||||
end
|
end
|
||||||
if (checkbox_copy_gps_lon.value) then
|
if (label_copy_gps_lon.value) then
|
||||||
image.longitude = entry_gps_lon.text
|
image.longitude = copy_gps_longitude
|
||||||
end
|
end
|
||||||
if (checkbox_copy_gps_ele.value) then
|
if (label_copy_gps_ele.value) then
|
||||||
image.elevation = entry_gps_ele.text
|
image.elevation = copy_gps_elevation
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -356,7 +338,7 @@ local function open_location_in_gnome_maps()
|
|||||||
local i = 0;
|
local i = 0;
|
||||||
|
|
||||||
-- Use the first image with geo information
|
-- Use the first image with geo information
|
||||||
for jj,image in ipairs(sel_images) do
|
for _,image in ipairs(sel_images) do
|
||||||
if ((image.longitude and image.latitude) and
|
if ((image.longitude and image.latitude) and
|
||||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||||
) then
|
) then
|
||||||
@@ -383,12 +365,12 @@ end
|
|||||||
local function reverse_geocode()
|
local function reverse_geocode()
|
||||||
|
|
||||||
if not df.check_if_bin_exists("curl") then
|
if not df.check_if_bin_exists("curl") then
|
||||||
dt.print_error("curl not found")
|
dt.print_error(_("curl not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if not df.check_if_bin_exists("jq") then
|
if not df.check_if_bin_exists("jq") then
|
||||||
dt.print_error("jq not found")
|
dt.print_error(_("jq not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -399,7 +381,7 @@ local function reverse_geocode()
|
|||||||
local i = 0;
|
local i = 0;
|
||||||
|
|
||||||
-- Use the first image with geo information
|
-- Use the first image with geo information
|
||||||
for jj,image in ipairs(sel_images) do
|
for _,image in ipairs(sel_images) do
|
||||||
if ((image.longitude and image.latitude) and
|
if ((image.longitude and image.latitude) and
|
||||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||||
) then
|
) then
|
||||||
@@ -474,7 +456,7 @@ local function calc_distance()
|
|||||||
|
|
||||||
local sel_images = dt.gui.selection()
|
local sel_images = dt.gui.selection()
|
||||||
|
|
||||||
for jj,image in ipairs(sel_images) do
|
for _,image in ipairs(sel_images) do
|
||||||
if ((image.longitude and image.latitude) and
|
if ((image.longitude and image.latitude) and
|
||||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||||
) then
|
) then
|
||||||
@@ -506,12 +488,12 @@ local function calc_distance()
|
|||||||
|
|
||||||
if (distance < 1) then
|
if (distance < 1) then
|
||||||
distance = distance * 1000
|
distance = distance * 1000
|
||||||
distanceUnit = "m"
|
distanceUnit = _("m")
|
||||||
else
|
else
|
||||||
distanceUnit = "km"
|
distanceUnit = _("km")
|
||||||
end
|
end
|
||||||
|
|
||||||
return string.format(_("distance: %.2f %s"), distance, distanceUnit)
|
return string.format(_("Distance: %.2f %s"), distance, distanceUnit)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function print_calc_distance()
|
local function print_calc_distance()
|
||||||
@@ -534,12 +516,12 @@ local altitude_filename = dt.new_widget("entry")
|
|||||||
text = "altitude.csv",
|
text = "altitude.csv",
|
||||||
placeholder = "altitude.csv",
|
placeholder = "altitude.csv",
|
||||||
editable = true,
|
editable = true,
|
||||||
tooltip = _("name of the exported file"),
|
tooltip = _("Name of the exported file"),
|
||||||
reset_callback = function(self) self.text = "text" end
|
reset_callback = function(self) self.text = "text" end
|
||||||
}
|
}
|
||||||
|
|
||||||
local function altitude_profile()
|
local function altitude_profile()
|
||||||
dt.print(_("start export"))
|
dt.print(_("Start export"))
|
||||||
local sel_images = dt.gui.action_images
|
local sel_images = dt.gui.action_images
|
||||||
|
|
||||||
local lat1 = 0;
|
local lat1 = 0;
|
||||||
@@ -558,7 +540,7 @@ local function altitude_profile()
|
|||||||
local elevationAdd = 0;
|
local elevationAdd = 0;
|
||||||
|
|
||||||
local sel_images = dt.gui.action_images
|
local sel_images = dt.gui.action_images
|
||||||
for jj,image in ipairs(sel_images) do
|
for _,image in ipairs(sel_images) do
|
||||||
if ((not isnan(image.longitude) and not isnan(image.latitude) and not isnan(image.elevation) and image.elevation) and
|
if ((not isnan(image.longitude) and not isnan(image.latitude) and not isnan(image.elevation) and image.elevation) and
|
||||||
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
|
||||||
) then
|
) then
|
||||||
@@ -600,7 +582,7 @@ local function altitude_profile()
|
|||||||
file = io.open(exportDirectory.."/"..exportFilename, "w")
|
file = io.open(exportDirectory.."/"..exportFilename, "w")
|
||||||
file:write(csv_file)
|
file:write(csv_file)
|
||||||
file:close()
|
file:close()
|
||||||
dt.print(string.format(_("file created in %s"), exportDirectory))
|
dt.print(_("File created in ")..exportDirectory)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -608,7 +590,7 @@ local function install_module()
|
|||||||
if not gT.module_installed then
|
if not gT.module_installed then
|
||||||
dt.register_lib(
|
dt.register_lib(
|
||||||
"geoToolbox", -- Module name
|
"geoToolbox", -- Module name
|
||||||
_("geo toolbox"), -- name
|
"geo toolbox", -- name
|
||||||
true, -- expandable
|
true, -- expandable
|
||||||
false, -- resetable
|
false, -- resetable
|
||||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||||
@@ -630,14 +612,14 @@ end
|
|||||||
|
|
||||||
local function restart()
|
local function restart()
|
||||||
dt.register_event("geoToolbox_cd", "shortcut",
|
dt.register_event("geoToolbox_cd", "shortcut",
|
||||||
print_calc_distance, _("calculate the distance from latitude and longitude in km"))
|
print_calc_distance, _("Calculate the distance from latitude and longitude in km"))
|
||||||
dt.register_event("geoToolbox", "mouse-over-image-changed",
|
dt.register_event("geoToolbox", "mouse-over-image-changed",
|
||||||
toolbox_calc_distance)
|
toolbox_calc_distance)
|
||||||
|
|
||||||
dt.register_event("geoToolbox_wg", "shortcut",
|
dt.register_event("geoToolbox_wg", "shortcut",
|
||||||
select_with_gps, _("select all images with GPS information"))
|
select_with_gps, _("Select all images with GPS information"))
|
||||||
dt.register_event("geoToolbox_ng", "shortcut",
|
dt.register_event("geoToolbox_ng", "shortcut",
|
||||||
select_without_gps, _("select all images without GPS information"))
|
select_without_gps, _("Select all images without GPS information"))
|
||||||
|
|
||||||
dt.gui.libs["geoToolbox"].visible = true
|
dt.gui.libs["geoToolbox"].visible = true
|
||||||
end
|
end
|
||||||
@@ -660,41 +642,29 @@ gT.widget = dt.new_widget("box")
|
|||||||
dt.new_widget("button")
|
dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = _("select geo images"),
|
label = _("select geo images"),
|
||||||
tooltip = _("select all images with GPS information"),
|
tooltip = _("Select all images with GPS information"),
|
||||||
clicked_callback = select_with_gps
|
clicked_callback = select_with_gps
|
||||||
},
|
},
|
||||||
dt.new_widget("button")
|
dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = _("select non-geo images"),
|
label = _("select non-geo images"),
|
||||||
tooltip = _("select all images without GPS information"),
|
tooltip = _("Select all images without GPS information"),
|
||||||
clicked_callback = select_without_gps
|
clicked_callback = select_without_gps
|
||||||
},
|
},
|
||||||
separator,--------------------------------------------------------
|
separator,--------------------------------------------------------
|
||||||
dt.new_widget("button")
|
dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = _("copy GPS data"),
|
label = _("copy GPS data"),
|
||||||
tooltip = _("copy GPS data"),
|
tooltip = _("Copy GPS data"),
|
||||||
clicked_callback = copy_gps
|
clicked_callback = copy_gps
|
||||||
},
|
},
|
||||||
dt.new_widget("box"){
|
label_copy_gps_lat,
|
||||||
orientation = "horizontal",
|
label_copy_gps_lon,
|
||||||
checkbox_copy_gps_lat,
|
label_copy_gps_ele,
|
||||||
entry_gps_lat
|
|
||||||
},
|
|
||||||
dt.new_widget("box"){
|
|
||||||
orientation = "horizontal",
|
|
||||||
checkbox_copy_gps_lon,
|
|
||||||
entry_gps_lon
|
|
||||||
},
|
|
||||||
dt.new_widget("box"){
|
|
||||||
orientation = "horizontal",
|
|
||||||
checkbox_copy_gps_ele,
|
|
||||||
entry_gps_ele
|
|
||||||
},
|
|
||||||
dt.new_widget("button")
|
dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = _("apply GPS data to image"),
|
label = _("paste GPS data"),
|
||||||
tooltip = _("apply GPS data to image"),
|
tooltip = _("Paste GPS data"),
|
||||||
clicked_callback = paste_gps
|
clicked_callback = paste_gps
|
||||||
},
|
},
|
||||||
separator2,--------------------------------------------------------
|
separator2,--------------------------------------------------------
|
||||||
@@ -724,14 +694,14 @@ gT.widget = dt.new_widget("box")
|
|||||||
dt.new_widget("button")
|
dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = _("open in Gnome Maps"),
|
label = _("open in Gnome Maps"),
|
||||||
tooltip = _("open location in Gnome Maps"),
|
tooltip = _("Open location in Gnome Maps"),
|
||||||
clicked_callback = open_location_in_gnome_maps
|
clicked_callback = open_location_in_gnome_maps
|
||||||
},
|
},
|
||||||
separator4,--------------------------------------------------------
|
separator4,--------------------------------------------------------
|
||||||
dt.new_widget("button")
|
dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = _("reverse geocode"),
|
label = _("reverse geocode"),
|
||||||
tooltip = _("this just shows the name of the location, but doesn't add it as tag"),
|
tooltip = _("This just shows the name of the location, but doesn't add it as tag"),
|
||||||
clicked_callback = reverse_geocode
|
clicked_callback = reverse_geocode
|
||||||
},
|
},
|
||||||
separator5,--------------------------------------------------------
|
separator5,--------------------------------------------------------
|
||||||
@@ -741,7 +711,7 @@ gT.widget = dt.new_widget("box")
|
|||||||
dt.new_widget("button")
|
dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = _("export altitude CSV file"),
|
label = _("export altitude CSV file"),
|
||||||
tooltip = _("create an altitude profile using the GPS data in the metadata"),
|
tooltip = _("Create an altitude profile using the GPS data in the metadata"),
|
||||||
clicked_callback = altitude_profile
|
clicked_callback = altitude_profile
|
||||||
},
|
},
|
||||||
labelDistance
|
labelDistance
|
||||||
@@ -769,19 +739,19 @@ dt.preferences.register("geoToolbox",
|
|||||||
"mapBoxKey",
|
"mapBoxKey",
|
||||||
"string",
|
"string",
|
||||||
_("geoToolbox export: MapBox Key"),
|
_("geoToolbox export: MapBox Key"),
|
||||||
"https://www.mapbox.com/studio/account/tokens",
|
_("https://www.mapbox.com/studio/account/tokens"),
|
||||||
'' )
|
'' )
|
||||||
|
|
||||||
-- Register
|
-- Register
|
||||||
dt.register_event("geoToolbox_cd", "shortcut",
|
dt.register_event("geoToolbox_cd", "shortcut",
|
||||||
print_calc_distance, _("calculate the distance from latitude and longitude in km"))
|
print_calc_distance, _("Calculate the distance from latitude and longitude in km"))
|
||||||
dt.register_event("geoToolbox", "mouse-over-image-changed",
|
dt.register_event("geoToolbox", "mouse-over-image-changed",
|
||||||
toolbox_calc_distance)
|
toolbox_calc_distance)
|
||||||
|
|
||||||
dt.register_event("geoToolbox_wg", "shortcut",
|
dt.register_event("geoToolbox_wg", "shortcut",
|
||||||
select_with_gps, _("select all images with GPS information"))
|
select_with_gps, _("Select all images with GPS information"))
|
||||||
dt.register_event("geoToolbox_ng", "shortcut",
|
dt.register_event("geoToolbox_ng", "shortcut",
|
||||||
select_without_gps, _("select all images without GPS information"))
|
select_without_gps, _("Select all images without GPS information"))
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
script_data.restart = restart
|
script_data.restart = restart
|
||||||
|
|||||||
@@ -69,31 +69,27 @@ local dt = require "darktable"
|
|||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dtsys = require "lib/dtutils.system"
|
local dtsys = require "lib/dtutils.system"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
local gimp_widget = nil
|
local gimp_widget = nil
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "gimp")
|
du.check_min_api_version("7.0.0", "gimp")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("edit with GIMP"),
|
|
||||||
purpose = _("export and edit with GIMP"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/gimp"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("gimp",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("gimp", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local function group_if_not_member(img, new_img)
|
local function group_if_not_member(img, new_img)
|
||||||
local image_table = img:get_group_members()
|
local image_table = img:get_group_members()
|
||||||
local is_member = false
|
local is_member = false
|
||||||
@@ -113,7 +109,7 @@ end
|
|||||||
|
|
||||||
local function show_status(storage, image, format, filename,
|
local function show_status(storage, image, format, filename,
|
||||||
number, total, high_quality, extra_data)
|
number, total, high_quality, extra_data)
|
||||||
dt.print(string.format(_("export image %i/%i"), number, total))
|
dt.print(string.format(_("Export Image %i/%i"), number, total))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function gimp_edit(storage, image_table, extra_data) --finalize
|
local function gimp_edit(storage, image_table, extra_data) --finalize
|
||||||
@@ -123,7 +119,7 @@ local function gimp_edit(storage, image_table, extra_data) --finalize
|
|||||||
local gimp_executable = df.check_if_bin_exists("gimp")
|
local gimp_executable = df.check_if_bin_exists("gimp")
|
||||||
|
|
||||||
if not gimp_executable then
|
if not gimp_executable then
|
||||||
dt.print_error("GIMP not found")
|
dt.print_error(_("GIMP not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -146,7 +142,7 @@ local function gimp_edit(storage, image_table, extra_data) --finalize
|
|||||||
img_list = img_list ..exp_img.. " "
|
img_list = img_list ..exp_img.. " "
|
||||||
end
|
end
|
||||||
|
|
||||||
dt.print(_("launching GIMP..."))
|
dt.print(_("Launching GIMP..."))
|
||||||
|
|
||||||
local gimpStartCommand
|
local gimpStartCommand
|
||||||
gimpStartCommand = gimp_executable .. " " .. img_list
|
gimpStartCommand = gimp_executable .. " " .. img_list
|
||||||
@@ -217,7 +213,7 @@ gimp_widget = dt.new_widget("check_button"){
|
|||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
dt.register_storage("module_gimp", _("edit with GIMP"), show_status, gimp_edit, nil, nil, gimp_widget)
|
dt.register_storage("module_gimp", _("Edit with GIMP"), show_status, gimp_edit, nil, nil, gimp_widget)
|
||||||
|
|
||||||
--
|
--
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|||||||
@@ -25,30 +25,26 @@ For each source folder, a separate <trk> is generated in the gpx file.
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dl = require "lib/dtutils"
|
local dl = require "lib/dtutils"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
dl.check_min_api_version("7.0.0", "gpx_export")
|
dl.check_min_api_version("7.0.0", "gpx_export")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("gpx export"),
|
|
||||||
purpose = _("export gpx information to a file"),
|
|
||||||
author = "Jannis_V",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/gpx_export"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("gpx_export",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("gpx_export", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local gpx = {}
|
local gpx = {}
|
||||||
gpx.module_installed = false
|
gpx.module_installed = false
|
||||||
gpx.event_registered = false
|
gpx.event_registered = false
|
||||||
@@ -136,11 +132,11 @@ local function create_gpx_file()
|
|||||||
|
|
||||||
local file = io.open(path, "w")
|
local file = io.open(path, "w")
|
||||||
if (file == nil) then
|
if (file == nil) then
|
||||||
dt.print(string.format(_("invalid path: %s"), path))
|
dt.print(_("invalid path: ")..path)
|
||||||
else
|
else
|
||||||
file:write(gpx_file)
|
file:write(gpx_file)
|
||||||
file:close()
|
file:close()
|
||||||
dt.print(string.format(_("gpx file created: "), path))
|
dt.print(_("gpx file created: ")..path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -148,7 +144,7 @@ local function install_module()
|
|||||||
if not gpx.module_installed then
|
if not gpx.module_installed then
|
||||||
dt.register_lib(
|
dt.register_lib(
|
||||||
"gpx_exporter",
|
"gpx_exporter",
|
||||||
_("gpx export"),
|
"gpx export",
|
||||||
true, -- expandable
|
true, -- expandable
|
||||||
true, -- resetable
|
true, -- resetable
|
||||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||||
|
|||||||
@@ -32,28 +32,17 @@ USAGE
|
|||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("2.0.0", "harmonic_armature_guide")
|
du.check_min_api_version("2.0.0", "harmonic_armature_guide")
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("harmonic_armature_guide",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext("harmonic_armature_guide", msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("harmonic armature guide"),
|
|
||||||
purpose = _("harmonic artmature guide"),
|
|
||||||
author = "Hubert Kowalski",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/harmonic_armature_guide"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
dt.guides.register_guide("harmonic armature",
|
dt.guides.register_guide("harmonic armature",
|
||||||
-- draw
|
-- draw
|
||||||
function(cairo, x, y, width, height, zoom_scale)
|
function(cairo, x, y, width, height, zoom_scale)
|
||||||
@@ -86,12 +75,4 @@ end,
|
|||||||
function()
|
function()
|
||||||
return dt.new_widget("label"){label = _("harmonic armature"), halign = "start"}
|
return dt.new_widget("label"){label = _("harmonic armature"), halign = "start"}
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
-- nothing to destroy
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -58,34 +58,30 @@ local MODULE = "hif_group_leader"
|
|||||||
du.check_min_api_version("7.0.0", MODULE)
|
du.check_min_api_version("7.0.0", MODULE)
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("HIF group leader"),
|
|
||||||
purpose = _("make hif image group leader"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/hif_group_leader"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
-- I 1 8 N
|
||||||
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain(MODULE, dt.configuration.config_dir .. "/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext(MODULE, msgid)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- P R E F E R E N C E S
|
-- P R E F E R E N C E S
|
||||||
@@ -186,7 +182,7 @@ dt.register_event(MODULE .. "_collect", "shortcut",
|
|||||||
local images = dt.collection
|
local images = dt.collection
|
||||||
make_existing_hif_group_leader(images)
|
make_existing_hif_group_leader(images)
|
||||||
end,
|
end,
|
||||||
_("make hif group leader for collection")
|
_("Make hif group leader for collection")
|
||||||
)
|
)
|
||||||
|
|
||||||
dt.register_event(MODULE .. "_select", "shortcut",
|
dt.register_event(MODULE .. "_select", "shortcut",
|
||||||
@@ -194,7 +190,7 @@ dt.register_event(MODULE .. "_select", "shortcut",
|
|||||||
local images = dt.gui.selection()
|
local images = dt.gui.selection()
|
||||||
make_existing_hif_group_leader(images)
|
make_existing_hif_group_leader(images)
|
||||||
end,
|
end,
|
||||||
_("make hif group leader for selection")
|
_("Make hif group leader for selection")
|
||||||
)
|
)
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
@@ -40,7 +40,7 @@ local du = require "lib/dtutils"
|
|||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local log = require "lib/dtutils.log"
|
local log = require "lib/dtutils.log"
|
||||||
local dtsys = require "lib/dtutils.system"
|
local dtsys = require "lib/dtutils.system"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
local namespace = 'module_hugin'
|
local namespace = 'module_hugin'
|
||||||
local user_pref_str = 'prefer_gui'
|
local user_pref_str = 'prefer_gui'
|
||||||
@@ -56,26 +56,22 @@ local PQ = dt.configuration.running_os == "windows" and '"' or "'"
|
|||||||
-- works with darktable API version from 5.0.0 on
|
-- works with darktable API version from 5.0.0 on
|
||||||
du.check_min_api_version("7.0.0", "hugin")
|
du.check_min_api_version("7.0.0", "hugin")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("hugin"),
|
|
||||||
purpose = _("stitch images into a panorama"),
|
|
||||||
author = "Wolfgang Goetz",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/hugin"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("hugin",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("hugin", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local function user_preference_changed(widget)
|
local function user_preference_changed(widget)
|
||||||
user_prefer_gui = widget.value
|
user_prefer_gui = widget.value
|
||||||
dt.preferences.write(namespace, user_pref_str, "bool", user_prefer_gui)
|
dt.preferences.write(namespace, user_pref_str, "bool", user_prefer_gui)
|
||||||
@@ -83,7 +79,7 @@ end
|
|||||||
|
|
||||||
local function show_status(storage, image, format, filename,
|
local function show_status(storage, image, format, filename,
|
||||||
number, total, high_quality, extra_data)
|
number, total, high_quality, extra_data)
|
||||||
dt.print(string.format(_("exporting to hugin: %d / %d"), number, total))
|
dt.print("exporting to hugin: "..tostring(number).."/"..tostring(total))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function create_panorama(storage, image_table, extra_data) --finalize
|
local function create_panorama(storage, image_table, extra_data) --finalize
|
||||||
@@ -144,7 +140,7 @@ local function create_panorama(storage, image_table, extra_data) --finalize
|
|||||||
end
|
end
|
||||||
|
|
||||||
if first_file == nil then
|
if first_file == nil then
|
||||||
dt.print(_("no file selected"))
|
dt.print("no file selected")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -205,7 +201,7 @@ local function create_panorama(storage, image_table, extra_data) --finalize
|
|||||||
if df.check_if_file_exists(src_path) then
|
if df.check_if_file_exists(src_path) then
|
||||||
log.msg(log.debug, "found ", src_path, " importing to ", dst_path)
|
log.msg(log.debug, "found ", src_path, " importing to ", dst_path)
|
||||||
df.file_move(src_path, dst_path)
|
df.file_move(src_path, dst_path)
|
||||||
dt.print(string.format(_("importing file %s"), dst_path))
|
dt.print(_("importing file "..dst_path))
|
||||||
dt.database.import(dst_path)
|
dt.database.import(dst_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -231,7 +227,7 @@ hugin_widget = dt.new_widget("box") {
|
|||||||
orientation = "vertical",
|
orientation = "vertical",
|
||||||
dt.new_widget("check_button")
|
dt.new_widget("check_button")
|
||||||
{
|
{
|
||||||
label = _("launch hugin gui"),
|
label = _(" launch hugin gui"),
|
||||||
value = user_prefer_gui,
|
value = user_prefer_gui,
|
||||||
tooltip = _('launch hugin in gui mode'),
|
tooltip = _('launch hugin in gui mode'),
|
||||||
clicked_callback = user_preference_changed
|
clicked_callback = user_preference_changed
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ local dt = require "darktable"
|
|||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dtsys = require "lib/dtutils.system"
|
local dtsys = require "lib/dtutils.system"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
local job = nil
|
local job = nil
|
||||||
|
|
||||||
-- path separator constant
|
-- path separator constant
|
||||||
@@ -73,26 +73,22 @@ local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
|||||||
-- works with LUA API version 5.0.0
|
-- works with LUA API version 5.0.0
|
||||||
du.check_min_api_version("7.0.0", "image_stack")
|
du.check_min_api_version("7.0.0", "image_stack")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("image stack"),
|
|
||||||
purpose = _("process a stack of images"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/image_stack"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("image_stack",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("image_stack", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- GUI definitions
|
-- GUI definitions
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@@ -139,19 +135,19 @@ local chkbtn_will_align = dt.new_widget("check_button"){
|
|||||||
local chkbtn_radial_distortion = dt.new_widget("check_button"){
|
local chkbtn_radial_distortion = dt.new_widget("check_button"){
|
||||||
label = _('optimize radial distortion for all images'),
|
label = _('optimize radial distortion for all images'),
|
||||||
value = dt.preferences.read("align_image_stack", "def_radial_distortion", "bool"),
|
value = dt.preferences.read("align_image_stack", "def_radial_distortion", "bool"),
|
||||||
tooltip = _('optimize radial distortion for all images, \nexcept the first'),
|
tooltip = _('optimize radial distortion for all images, \nexcept for first'),
|
||||||
}
|
}
|
||||||
|
|
||||||
local chkbtn_optimize_field = dt.new_widget("check_button"){
|
local chkbtn_optimize_field = dt.new_widget("check_button"){
|
||||||
label = _('optimize field of view for all images'),
|
label = _('optimize field of view for all images'),
|
||||||
value = dt.preferences.read("align_image_stack", "def_optimize_field", "bool"),
|
value = dt.preferences.read("align_image_stack", "def_optimize_field", "bool"),
|
||||||
tooltip =_('optimize field of view for all images, except the first. \nUseful for aligning focus stacks (DFF) with slightly \ndifferent magnification.'),
|
tooltip =_('optimize field of view for all images, except for first. \nUseful for aligning focus stacks (DFF) with slightly \ndifferent magnification.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
local chkbtn_optimize_image_center = dt.new_widget("check_button"){
|
local chkbtn_optimize_image_center = dt.new_widget("check_button"){
|
||||||
label = _('optimize image center shift for all images'),
|
label = _('optimize image center shift for all images'),
|
||||||
value = dt.preferences.read("align_image_stack", "def_optimize_image_center", "bool"),
|
value = dt.preferences.read("align_image_stack", "def_optimize_image_center", "bool"),
|
||||||
tooltip =_('optimize image center shift for all images, \nexcept the first.'),
|
tooltip =_('optimize image center shift for all images, \nexcept for first.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
local chkbtn_auto_crop = dt.new_widget("check_button"){
|
local chkbtn_auto_crop = dt.new_widget("check_button"){
|
||||||
@@ -263,7 +259,7 @@ end
|
|||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
||||||
dt.print(string.format(_("export image %i/%i"), number, total))
|
dt.print(string.format(_("Export Image %i/%i"), number, total))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- read the gui and populate the align_image_stack arguments
|
-- read the gui and populate the align_image_stack arguments
|
||||||
@@ -456,7 +452,7 @@ local function copy_image_attributes(from, to, ...)
|
|||||||
to.rights = from.rights
|
to.rights = from.rights
|
||||||
to.description = from.description
|
to.description = from.description
|
||||||
else
|
else
|
||||||
dt.print_error("Unrecognized option to copy_image_attributes: " .. arg)
|
dt.print_error(_("Unrecognized option to copy_image_attributes: " .. arg))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -500,11 +496,11 @@ local function image_stack(storage, image_table, extra_data)
|
|||||||
|
|
||||||
if image_count < 2 then
|
if image_count < 2 then
|
||||||
dt.print(_("ERROR: at least 2 images required for image stacking, exiting..."))
|
dt.print(_("ERROR: at least 2 images required for image stacking, exiting..."))
|
||||||
dt.print_error(image_count .. " image(s) selected, at least 2 required")
|
dt.print_error(_(image_count .. " image(s) selected, at least 2 required"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
job = dt.gui.create_job(_("image stack"), true, stop_job)
|
job = dt.gui.create_job("image stack", true, stop_job)
|
||||||
job.percent = job.percent + percent_step
|
job.percent = job.percent + percent_step
|
||||||
|
|
||||||
-- align images if requested
|
-- align images if requested
|
||||||
@@ -524,13 +520,13 @@ local function image_stack(storage, image_table, extra_data)
|
|||||||
job.percent = job.percent + percent_step
|
job.percent = job.percent + percent_step
|
||||||
else
|
else
|
||||||
dt.print(_("ERROR: image alignment failed"))
|
dt.print(_("ERROR: image alignment failed"))
|
||||||
dt.print_error("image alignment failed")
|
dt.print_error(_("image alignment failed"))
|
||||||
cleanup(img_list)
|
cleanup(img_list)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
dt.print(_("ERROR: align_image_stack not found"))
|
dt.print(_("ERROR: align_image_stack not found"))
|
||||||
dt.print_error("align_image_stack not found")
|
dt.print_error(_("align_image_stack not found"))
|
||||||
cleanup(img_list)
|
cleanup(img_list)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -544,7 +540,7 @@ local function image_stack(storage, image_table, extra_data)
|
|||||||
local ignore_tif_tags = " -quiet -define tiff:ignore-tags=40965,42032,42033,42034,42036,18246,18249,36867,34864,34866 "
|
local ignore_tif_tags = " -quiet -define tiff:ignore-tags=40965,42032,42033,42034,42036,18246,18249,36867,34864,34866 "
|
||||||
if convert_executable then
|
if convert_executable then
|
||||||
local convert_command = convert_executable .. ignore_tif_tags .. convert_arguments
|
local convert_command = convert_executable .. ignore_tif_tags .. convert_arguments
|
||||||
dt.print_log("convert command is " .. convert_command)
|
dt.print_log(_("convert command is " .. convert_command))
|
||||||
dt.print(_("processing image stack"))
|
dt.print(_("processing image stack"))
|
||||||
local result = dtsys.external_command(convert_command)
|
local result = dtsys.external_command(convert_command)
|
||||||
if result == 0 then
|
if result == 0 then
|
||||||
@@ -558,7 +554,7 @@ local function image_stack(storage, image_table, extra_data)
|
|||||||
local import_filename = df.create_unique_filename(film_roll_path .. PS .. df.get_filename(output_filename))
|
local import_filename = df.create_unique_filename(film_roll_path .. PS .. df.get_filename(output_filename))
|
||||||
df.file_move(output_filename, import_filename)
|
df.file_move(output_filename, import_filename)
|
||||||
imported_image = dt.database.import(import_filename)
|
imported_image = dt.database.import(import_filename)
|
||||||
local created_tag = dt.tags.create(_("created with|image_stack"))
|
local created_tag = dt.tags.create(_("Created with|image_stack"))
|
||||||
dt.tags.attach(created_tag, imported_image)
|
dt.tags.attach(created_tag, imported_image)
|
||||||
-- all the images are the same except for time, so just copy the attributes
|
-- all the images are the same except for time, so just copy the attributes
|
||||||
-- from the first
|
-- from the first
|
||||||
@@ -572,7 +568,7 @@ local function image_stack(storage, image_table, extra_data)
|
|||||||
|
|
||||||
if tag_source then
|
if tag_source then
|
||||||
dt.print(_("tagging source images"))
|
dt.print(_("tagging source images"))
|
||||||
local source_tag = dt.tags.create(_("source file|" .. imported_image.filename))
|
local source_tag = dt.tags.create(_("Source file|" .. imported_image.filename))
|
||||||
for img, _ in pairs(image_table) do
|
for img, _ in pairs(image_table) do
|
||||||
dt.tags.attach(source_tag, img)
|
dt.tags.attach(source_tag, img)
|
||||||
end
|
end
|
||||||
@@ -584,7 +580,7 @@ local function image_stack(storage, image_table, extra_data)
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
dt.print(_("ERROR: convert executable not found"))
|
dt.print(_("ERROR: convert executable not found"))
|
||||||
dt.print_error("convert executable not found")
|
dt.print_error(_("convert executable not found"))
|
||||||
cleanup(img_list)
|
cleanup(img_list)
|
||||||
end
|
end
|
||||||
job.valid = false
|
job.valid = false
|
||||||
@@ -596,7 +592,7 @@ end
|
|||||||
|
|
||||||
dt.preferences.register("align_image_stack", "align_use_gpu", -- name
|
dt.preferences.register("align_image_stack", "align_use_gpu", -- name
|
||||||
"bool", -- type
|
"bool", -- type
|
||||||
_('align image stack: use GPU for remapping'), -- label
|
_('align image stack: use GPU for remaping'), -- label
|
||||||
_('set the GPU remapping for image align'), -- tooltip
|
_('set the GPU remapping for image align'), -- tooltip
|
||||||
false)
|
false)
|
||||||
|
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ local dt = require "darktable"
|
|||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local ds = require "lib/dtutils.string"
|
local ds = require "lib/dtutils.string"
|
||||||
local dtsys = require "lib/dtutils.system"
|
local gettext = dt.gettext
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local img_time = {}
|
local img_time = {}
|
||||||
img_time.module_installed = false
|
img_time.module_installed = false
|
||||||
@@ -116,27 +115,22 @@ img_time.event_registered = false
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", "image_time")
|
du.check_min_api_version("7.0.0", "image_time")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("image time"),
|
|
||||||
purpose = _("synchronize image time for images shot with different cameras or adjust or set image time"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/image_time"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("image_time",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("image_time", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local PS = dt.configuration.runnin_os == "windows" and "\\" or "/"
|
local PS = dt.configuration.runnin_os == "windows" and "\\" or "/"
|
||||||
local ERROR = -1
|
local ERROR = -1
|
||||||
@@ -154,12 +148,12 @@ local function systime2exiftime(systime)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function vars2exiftime(year, month, day, hour, min, sec)
|
local function vars2exiftime(year, month, day, hour, min, sec)
|
||||||
local y = tonumber(year) and string.format("%4d", year) or "0000"
|
local y = tonumber(year) and string.format("%4d", year) or " "
|
||||||
local mo = tonumber(month) and string.format("%02d", month) or "00"
|
local mo = tonumber(month) and string.format("%02d", month) or " "
|
||||||
local d = tonumber(day) and string.format("%02d", day) or "00"
|
local d = tonumber(day) and string.format("%02d", day) or " "
|
||||||
local h = tonumber(hour) and string.format("%02d", hour) or "00"
|
local h = tonumber(hour) and string.format("%02d", hour) or " "
|
||||||
local m = tonumber(min) and string.format("%02d", min) or "00"
|
local m = tonumber(min) and string.format("%02d", min) or " "
|
||||||
local s = tonumber(sec) and string.format("%02d", sec) or "00"
|
local s = tonumber(sec) and string.format("%02d", sec) or " "
|
||||||
return(y .. ":" .. mo .. ":" .. d .. " " .. h .. ":" .. m .. ":" .. s)
|
return(y .. ":" .. mo .. ":" .. d .. " " .. h .. ":" .. m .. ":" .. s)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -181,7 +175,7 @@ local function calculate_difference(images)
|
|||||||
img_time.diff_entry.text = calc_time_difference(images[1], images[2])
|
img_time.diff_entry.text = calc_time_difference(images[1], images[2])
|
||||||
img_time.btn.sensitive = true
|
img_time.btn.sensitive = true
|
||||||
else
|
else
|
||||||
dt.print(_("ERROR: 2 images must be selected"))
|
dt.print(_("Error: 2 images must be selected"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -193,7 +187,7 @@ end
|
|||||||
|
|
||||||
local function synchronize_time(images)
|
local function synchronize_time(images)
|
||||||
local sign = 1
|
local sign = 1
|
||||||
if img_time.sdir.value == _("subtract") then
|
if img_time.sdir.value == "subtract" then
|
||||||
sign = -1
|
sign = -1
|
||||||
end
|
end
|
||||||
synchronize_times(images, tonumber(img_time.diff_entry.text) * sign)
|
synchronize_times(images, tonumber(img_time.diff_entry.text) * sign)
|
||||||
@@ -211,6 +205,7 @@ local function year_month2months(year, month)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function months2year_month(months)
|
local function months2year_month(months)
|
||||||
|
dt.print_log("months is " .. months)
|
||||||
local year = math.floor(months / 12)
|
local year = math.floor(months / 12)
|
||||||
local month = months - (year * 12)
|
local month = months - (year * 12)
|
||||||
|
|
||||||
@@ -223,7 +218,7 @@ local function get_image_taken_time(image)
|
|||||||
|
|
||||||
local exiv2 = df.check_if_bin_exists("exiv2")
|
local exiv2 = df.check_if_bin_exists("exiv2")
|
||||||
if exiv2 then
|
if exiv2 then
|
||||||
p = io.popen(exiv2 .. " -K Exif.Image.DateTime " .. df.sanitize_filename(image.path .. PS .. image.filename))
|
p = io.popen(exiv2 .. " -K Exif.Image.DateTime " .. image.path .. PS .. image.filename)
|
||||||
if p then
|
if p then
|
||||||
for line in p:lines() do
|
for line in p:lines() do
|
||||||
if string.match(line, "Exif.Image.DateTime") then
|
if string.match(line, "Exif.Image.DateTime") then
|
||||||
@@ -254,7 +249,7 @@ local function _get_windows_image_file_creation_time(image)
|
|||||||
end
|
end
|
||||||
p:close()
|
p:close()
|
||||||
else
|
else
|
||||||
dt.print(string.format(_("unable to get information for %s"), image.filename))
|
dt.print(_("unable to get information for ") .. image.filename)
|
||||||
datetime = ERROR
|
datetime = ERROR
|
||||||
end
|
end
|
||||||
return datetime
|
return datetime
|
||||||
@@ -262,7 +257,7 @@ end
|
|||||||
|
|
||||||
local function _get_nix_image_file_creation_time(image)
|
local function _get_nix_image_file_creation_time(image)
|
||||||
local datetime = nil
|
local datetime = nil
|
||||||
local p = io.popen("ls -lL --time-style=full-iso " .. df.sanitize_filename(image.path .. PS .. image.filename))
|
local p = io.popen("ls -lL --time-style=full-iso " .. image.path .. PS .. image.filename)
|
||||||
if p then
|
if p then
|
||||||
for line in p:lines() do
|
for line in p:lines() do
|
||||||
if string.match(line, ds.sanitize_lua(image.filename)) then
|
if string.match(line, ds.sanitize_lua(image.filename)) then
|
||||||
@@ -271,7 +266,7 @@ local function _get_nix_image_file_creation_time(image)
|
|||||||
end
|
end
|
||||||
p:close()
|
p:close()
|
||||||
else
|
else
|
||||||
dt.print(string.format(_("unable to get information for %s"), image.filename))
|
dt.print(_("unable to get information for ") .. image.filename)
|
||||||
datetime = ERROR
|
datetime = ERROR
|
||||||
end
|
end
|
||||||
return datetime
|
return datetime
|
||||||
@@ -319,7 +314,7 @@ local function reset_time(images)
|
|||||||
image.exif_datetime_taken = get_original_image_time(image)
|
image.exif_datetime_taken = get_original_image_time(image)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
dt.print_error("reset time: no images selected")
|
dt.print_error(_("reset time: no images selected"))
|
||||||
dt.print(_("please select the images that need their time reset"))
|
dt.print(_("please select the images that need their time reset"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -435,8 +430,8 @@ end
|
|||||||
img_time.widgets = {
|
img_time.widgets = {
|
||||||
-- name, type, tooltip, placeholder,
|
-- name, type, tooltip, placeholder,
|
||||||
{"ayr", "combobox", _("years"), _("years to adjust by, 0 - ?"), {seq(0,20)}, 1},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"asc", "combobox", _("seconds"), _("seconds to adjust by, 0-59"), {seq(0,59)}, 1},
|
||||||
@@ -462,13 +457,13 @@ end
|
|||||||
img_time.syr.selected = #img_time.syr
|
img_time.syr.selected = #img_time.syr
|
||||||
|
|
||||||
img_time.diff_entry = dt.new_widget("entry"){
|
img_time.diff_entry = dt.new_widget("entry"){
|
||||||
tooltip = _("time difference between images in seconds"),
|
tooltip = _("Time difference between images in seconds"),
|
||||||
placeholder = _("select 2 images and use the calculate button"),
|
placeholder = _("Select 2 images and use the calculate button"),
|
||||||
text = "",
|
text = "",
|
||||||
}
|
}
|
||||||
|
|
||||||
img_time.calc_btn = dt.new_widget("button"){
|
img_time.calc_btn = dt.new_widget("button"){
|
||||||
label = _("calculate"),
|
label = _("Calculate"),
|
||||||
tooltip = _("calculate time difference between 2 images"),
|
tooltip = _("calculate time difference between 2 images"),
|
||||||
clicked_callback = function()
|
clicked_callback = function()
|
||||||
calculate_difference(dt.gui.action_images)
|
calculate_difference(dt.gui.action_images)
|
||||||
@@ -508,7 +503,7 @@ img_time.stack = dt.new_widget("stack"){
|
|||||||
dt.new_widget("box"){
|
dt.new_widget("box"){
|
||||||
orientation = "vertical",
|
orientation = "vertical",
|
||||||
dt.new_widget("label"){label = _("set time")},
|
dt.new_widget("label"){label = _("set time")},
|
||||||
dt.new_widget("section_label"){label = _("date:")},
|
dt.new_widget("section_label"){label = _("date: ")},
|
||||||
img_time.sdy,
|
img_time.sdy,
|
||||||
img_time.smo,
|
img_time.smo,
|
||||||
img_time.syr,
|
img_time.syr,
|
||||||
|
|||||||
@@ -58,34 +58,30 @@ local MODULE = "jpg_group_leader"
|
|||||||
du.check_min_api_version("7.0.0", MODULE)
|
du.check_min_api_version("7.0.0", MODULE)
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("JPG group leader"),
|
|
||||||
purpose = _("make jpg image group leader"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/jpg_group_leader"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
-- I 1 8 N
|
||||||
|
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain(MODULE, dt.configuration.config_dir .. "/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext(MODULE, msgid)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- P R E F E R E N C E S
|
-- P R E F E R E N C E S
|
||||||
@@ -186,7 +182,7 @@ dt.register_event(MODULE .. "_collect", "shortcut",
|
|||||||
local images = dt.collection
|
local images = dt.collection
|
||||||
make_existing_jpg_group_leader(images)
|
make_existing_jpg_group_leader(images)
|
||||||
end,
|
end,
|
||||||
_("make jpg group leader for collection")
|
_("Make jpg group leader for collection")
|
||||||
)
|
)
|
||||||
|
|
||||||
dt.register_event(MODULE .. "_select", "shortcut",
|
dt.register_event(MODULE .. "_select", "shortcut",
|
||||||
@@ -194,7 +190,7 @@ dt.register_event(MODULE .. "_select", "shortcut",
|
|||||||
local images = dt.gui.selection()
|
local images = dt.gui.selection()
|
||||||
make_existing_jpg_group_leader(images)
|
make_existing_jpg_group_leader(images)
|
||||||
end,
|
end,
|
||||||
_("make jpg group leader for selection")
|
_("Make jpg group leader for selection")
|
||||||
)
|
)
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
@@ -39,34 +39,30 @@ local df = require "lib/dtutils.file"
|
|||||||
local ds = require "lib/dtutils.string"
|
local ds = require "lib/dtutils.string"
|
||||||
local dsys = require "lib/dtutils.system"
|
local dsys = require "lib/dtutils.system"
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "kml_export")
|
du.check_min_api_version("7.0.0", "kml_export")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("kml export"),
|
|
||||||
purpose = _("export KML/KMZ data to a file"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/kml_export"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("kml_export",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("kml_export", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
|
||||||
dt.print(string.format(_("export image %i/%i"), number, total))
|
dt.print(string.format(_("Export Image %i/%i"), number, total))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add duplicate index to filename
|
-- Add duplicate index to filename
|
||||||
@@ -93,12 +89,12 @@ local function create_kml_file(storage, image_table, extra_data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not df.check_if_bin_exists(magickPath) then
|
if not df.check_if_bin_exists(magickPath) then
|
||||||
dt.print_error("magick not found")
|
dt.print_error(_("magick not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if dt.configuration.running_os == "linux" then
|
if dt.configuration.running_os == "linux" then
|
||||||
if not df.check_if_bin_exists("xdg-user-dir") then
|
if not df.check_if_bin_exists("xdg-user-dir") then
|
||||||
dt.print_error("xdg-user-dir not found")
|
dt.print_error(_("xdg-user-dir not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -108,7 +104,7 @@ local function create_kml_file(storage, image_table, extra_data)
|
|||||||
if ( dt.preferences.read("kml_export","CreateKMZ","bool") == true
|
if ( dt.preferences.read("kml_export","CreateKMZ","bool") == true
|
||||||
and dt.configuration.running_os == "linux") then
|
and dt.configuration.running_os == "linux") then
|
||||||
if not df.check_if_bin_exists("zip") then
|
if not df.check_if_bin_exists("zip") then
|
||||||
dt.print_error("zip not found")
|
dt.print_error(_("zip not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
exportDirectory = dt.configuration.tmp_dir
|
exportDirectory = dt.configuration.tmp_dir
|
||||||
@@ -324,14 +320,14 @@ if dt.configuration.running_os == "windows" then
|
|||||||
"OpenKmlFile",
|
"OpenKmlFile",
|
||||||
"bool",
|
"bool",
|
||||||
_("KML export: Open KML file after export"),
|
_("KML export: Open KML file after export"),
|
||||||
_("opens the KML file after the export with the standard program for KML files"),
|
_("Opens the KML file after the export with the standard program for KML files"),
|
||||||
false )
|
false )
|
||||||
else
|
else
|
||||||
dt.preferences.register("kml_export",
|
dt.preferences.register("kml_export",
|
||||||
"OpenKmlFile",
|
"OpenKmlFile",
|
||||||
"bool",
|
"bool",
|
||||||
_("KML export: open KML/KMZ file after export"),
|
_("KML export: Open KML/KMZ file after export"),
|
||||||
_("opens the KML file after the export with the standard program for KML files"),
|
_("Opens the KML file after the export with the standard program for KML files"),
|
||||||
false )
|
false )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -350,23 +346,23 @@ end
|
|||||||
dt.preferences.register("kml_export",
|
dt.preferences.register("kml_export",
|
||||||
"ExportDirectory",
|
"ExportDirectory",
|
||||||
"directory",
|
"directory",
|
||||||
_("KML export: export directory"),
|
_("KML export: Export directory"),
|
||||||
_("a directory that will be used to export the KML/KMZ files"),
|
_("A directory that will be used to export the KML/KMZ files"),
|
||||||
defaultDir )
|
defaultDir )
|
||||||
|
|
||||||
if dt.configuration.running_os ~= "linux" then
|
if dt.configuration.running_os ~= "linux" then
|
||||||
dt.preferences.register("kml_export",
|
dt.preferences.register("kml_export",
|
||||||
"magickPath", -- name
|
"magickPath", -- name
|
||||||
"file", -- type
|
"file", -- type
|
||||||
_("KML export: ImageMagick binary location"), -- label
|
_("KML export: ImageMagick binary Location"), -- label
|
||||||
_("install location of magick[.exe], requires restart to take effect"), -- tooltip
|
_("Install location of magick[.exe]. Requires restart to take effect."), -- tooltip
|
||||||
"magick") -- default
|
"magick") -- default
|
||||||
end
|
end
|
||||||
|
|
||||||
dt.preferences.register("kml_export",
|
dt.preferences.register("kml_export",
|
||||||
"CreatePath",
|
"CreatePath",
|
||||||
"bool",
|
"bool",
|
||||||
_("KML export: connect images with path"),
|
_("KML export: Connect images with path"),
|
||||||
_("connect all images with a path"),
|
_("connect all images with a path"),
|
||||||
false )
|
false )
|
||||||
|
|
||||||
@@ -374,16 +370,16 @@ if dt.configuration.running_os == "linux" then
|
|||||||
dt.preferences.register("kml_export",
|
dt.preferences.register("kml_export",
|
||||||
"CreateKMZ",
|
"CreateKMZ",
|
||||||
"bool",
|
"bool",
|
||||||
_("KML export: create KMZ file"),
|
_("KML export: Create KMZ file"),
|
||||||
_("compress all imeges to one KMZ file"),
|
_("Compress all imeges to one KMZ file"),
|
||||||
true )
|
true )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Register
|
-- Register
|
||||||
if dt.configuration.running_os == "windows" then
|
if dt.configuration.running_os == "windows" then
|
||||||
dt.register_storage("kml_export", _("KML export"), nil, create_kml_file)
|
dt.register_storage("kml_export", _("KML Export"), nil, create_kml_file)
|
||||||
else
|
else
|
||||||
dt.register_storage("kml_export", _("KML/KMZ export"), nil, create_kml_file)
|
dt.register_storage("kml_export", _("KML/KMZ Export"), nil, create_kml_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|||||||
@@ -37,28 +37,17 @@ USAGE
|
|||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("2.0.0", "passport_guide")
|
du.check_min_api_version("2.0.0", "passport_guide")
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("passport_guide",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext("passport_guide", msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("passport guide"),
|
|
||||||
purpose = _("guides for cropping passport photos"),
|
|
||||||
author = "Kåre Hampf",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/passport_guide"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
dt.guides.register_guide("passport",
|
dt.guides.register_guide("passport",
|
||||||
-- draw
|
-- draw
|
||||||
function(cairo, x, y, width, height, zoom_scale)
|
function(cairo, x, y, width, height, zoom_scale)
|
||||||
@@ -102,13 +91,5 @@ function()
|
|||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
-- nothing to destroy
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
|
|
||||||
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on; hl Lua;
|
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on; hl Lua;
|
||||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||||
|
|||||||
@@ -40,29 +40,17 @@ USAGE
|
|||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("2.0.0", "passport_guide_germany")
|
du.check_min_api_version("2.0.0", "passport_guide_germany")
|
||||||
|
|
||||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("passport_guide_germany",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext("passport_guide_germany", msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("passport guide Germany"),
|
|
||||||
purpose = _("guides for cropping passport and ID card (\"Personalausweis\") photos based on the \"Passbild-Schablone\" from the German Federal Ministry of the Interior and Community"),
|
|
||||||
author = "menschmachine",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/passport_guide_germany"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
dt.guides.register_guide("Passport Photo Germany",
|
dt.guides.register_guide("Passport Photo Germany",
|
||||||
-- draw
|
-- draw
|
||||||
function(cairo, x, y, width, height, zoom_scale)
|
function(cairo, x, y, width, height, zoom_scale)
|
||||||
@@ -113,13 +101,5 @@ function()
|
|||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
-- noting to destroy
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
|
|
||||||
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on; hl Lua;
|
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on; hl Lua;
|
||||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||||
|
|||||||
@@ -42,14 +42,17 @@ local dt = require "darktable"
|
|||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("pdf_slideshow",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext("pdf_slideshow", msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not df.check_if_bin_exists("pdflatex") then
|
if not df.check_if_bin_exists("pdflatex") then
|
||||||
dt.print_error("pdflatex not found")
|
dt.print_error(_("pdflatex not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -59,13 +62,6 @@ du.check_min_api_version("7.0.0", "pdf_slideshow")
|
|||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("PDF slideshow"),
|
|
||||||
purpose = _("generates a PDF slideshow (via Latex) containing all selected images one per slide"),
|
|
||||||
author = "Pascal Obry",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/pdf_slideshow"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -235,7 +231,7 @@ dt.register_storage("pdf_slideshow",_("pdf slideshow"),
|
|||||||
local result = dt.control.execute(command)
|
local result = dt.control.execute(command)
|
||||||
if result ~= 0 then
|
if result ~= 0 then
|
||||||
dt.print(_("problem running pdflatex")) -- this one is probably usefull to the user
|
dt.print(_("problem running pdflatex")) -- this one is probably usefull to the user
|
||||||
error("problem running "..command)
|
error(_("problem running ")..command)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- open the PDF
|
-- open the PDF
|
||||||
@@ -245,7 +241,7 @@ dt.register_storage("pdf_slideshow",_("pdf slideshow"),
|
|||||||
local result = dt.control.execute(command)
|
local result = dt.control.execute(command)
|
||||||
if result ~= 0 then
|
if result ~= 0 then
|
||||||
dt.print(_("problem running pdf viewer")) -- this one is probably usefull to the user
|
dt.print(_("problem running pdf viewer")) -- this one is probably usefull to the user
|
||||||
error("problem running "..command)
|
error(_("problem running ")..command)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- finally do some clean-up
|
-- finally do some clean-up
|
||||||
|
|||||||
@@ -44,23 +44,10 @@ local dtsys = require "lib/dtutils.system"
|
|||||||
local MODULE_NAME = "photils"
|
local MODULE_NAME = "photils"
|
||||||
du.check_min_api_version("7.0.0", MODULE_NAME)
|
du.check_min_api_version("7.0.0", MODULE_NAME)
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("photils"),
|
|
||||||
purpose = _("suggest tags based on image classification"),
|
|
||||||
author = "Tobias Scheck",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/photils"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -68,6 +55,9 @@ script_data.show = nil -- only required for libs since the destroy_method only h
|
|||||||
|
|
||||||
|
|
||||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
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")
|
local exporter = dt.new_format("jpeg")
|
||||||
exporter.quality = 80
|
exporter.quality = 80
|
||||||
@@ -76,6 +66,10 @@ exporter.max_width = 224
|
|||||||
|
|
||||||
-- helper functions
|
-- helper functions
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext(MODULE_NAME, msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local function num_keys(tbl)
|
local function num_keys(tbl)
|
||||||
local num = 0
|
local num = 0
|
||||||
for _ in pairs(tbl) do num = num + 1 end
|
for _ in pairs(tbl) do num = num + 1 end
|
||||||
@@ -184,7 +178,7 @@ function PHOTILS.image_changed()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PHOTILS.tagged_image_has_changed()
|
function PHOTILS.tagged_image_has_changed()
|
||||||
GUI.warning.label = _("the suggested tags were not generated\n for the currently selected image!")
|
GUI.warning.label = _("The suggested tags were not generated\n for the currently selected image!")
|
||||||
end
|
end
|
||||||
|
|
||||||
function PHOTILS.paginate()
|
function PHOTILS.paginate()
|
||||||
@@ -241,7 +235,7 @@ end
|
|||||||
|
|
||||||
function PHOTILS.attach_tags()
|
function PHOTILS.attach_tags()
|
||||||
local num_selected = #dt.gui.selection()
|
local num_selected = #dt.gui.selection()
|
||||||
local job = dt.gui.create_job(_("apply tag to image"), true)
|
local job = dt.gui.create_job(_("Apply tag to image"), true)
|
||||||
|
|
||||||
for i = 1, num_selected, 1 do
|
for i = 1, num_selected, 1 do
|
||||||
local image = dt.gui.selection()[i]
|
local image = dt.gui.selection()[i]
|
||||||
@@ -253,7 +247,7 @@ function PHOTILS.attach_tags()
|
|||||||
job.percent = i / num_selected
|
job.percent = i / num_selected
|
||||||
end
|
end
|
||||||
|
|
||||||
dt.print(_("tags successfully attached to image"))
|
dt.print(_("Tags successfully attached to image"))
|
||||||
job.valid = false
|
job.valid = false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -264,6 +258,10 @@ function PHOTILS.get_tags(image, with_export)
|
|||||||
local out_arg = df.sanitize_filename(tmp_file)
|
local out_arg = df.sanitize_filename(tmp_file)
|
||||||
local executable = photils_installed
|
local executable = photils_installed
|
||||||
|
|
||||||
|
if dt.configuration.running_os == "macos" then
|
||||||
|
executable = executable .. "/Contents/MacOS/photils-cli"
|
||||||
|
end
|
||||||
|
|
||||||
if with_export then
|
if with_export then
|
||||||
dt.print_log("use export to for prediction")
|
dt.print_log("use export to for prediction")
|
||||||
local export_file = df.create_tmp_file()
|
local export_file = df.create_tmp_file()
|
||||||
@@ -315,11 +313,11 @@ function PHOTILS.on_tags_clicked()
|
|||||||
local images = dt.gui.selection()
|
local images = dt.gui.selection()
|
||||||
|
|
||||||
if #images == 0 then
|
if #images == 0 then
|
||||||
dt.print(_("no image selected."))
|
dt.print(_("No image selected."))
|
||||||
dt.control.sleep(2000)
|
dt.control.sleep(2000)
|
||||||
else
|
else
|
||||||
if #images > 1 then
|
if #images > 1 then
|
||||||
dt.print(_("this plugin can only handle a single image."))
|
dt.print(_("This plugin can only handle a single image."))
|
||||||
dt.gui.selection({images[1]})
|
dt.gui.selection({images[1]})
|
||||||
dt.control.sleep(2000)
|
dt.control.sleep(2000)
|
||||||
end
|
end
|
||||||
@@ -334,7 +332,7 @@ function PHOTILS.on_tags_clicked()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if #PHOTILS.tags == 0 then
|
if #PHOTILS.tags == 0 then
|
||||||
local msg = string.format(_("no tags were found"), MODULE_NAME)
|
local msg = string.format(_("no tags where found"), MODULE_NAME)
|
||||||
GUI.warning_label.label = msg
|
GUI.warning_label.label = msg
|
||||||
GUI.stack.active = GUI.error_view
|
GUI.stack.active = GUI.error_view
|
||||||
return
|
return
|
||||||
@@ -389,7 +387,7 @@ end
|
|||||||
local function install_module()
|
local function install_module()
|
||||||
if not PHOTILS.module_installed then
|
if not PHOTILS.module_installed then
|
||||||
dt.register_lib(MODULE_NAME,
|
dt.register_lib(MODULE_NAME,
|
||||||
_("photils auto-tagger"),
|
"photils autotagger",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
PHOTILS.plugin_display_views,
|
PHOTILS.plugin_display_views,
|
||||||
@@ -430,9 +428,9 @@ end
|
|||||||
|
|
||||||
if not photils_installed then
|
if not photils_installed then
|
||||||
GUI.warning_label.label = _("photils-cli not found")
|
GUI.warning_label.label = _("photils-cli not found")
|
||||||
dt.print_log("photils-cli not found")
|
dt.print_log(_("photils-cli not found"))
|
||||||
else
|
else
|
||||||
GUI.warning_label.label = _("select an image, click \"get tags\" and get \nsuggestions for tags.")
|
GUI.warning_label.label = _("Select an image, click \"get tags\" and get \nsuggestions for tags.")
|
||||||
end
|
end
|
||||||
|
|
||||||
GUI.pagination = dt.new_widget("box") {
|
GUI.pagination = dt.new_widget("box") {
|
||||||
@@ -475,9 +473,9 @@ dt.preferences.register(MODULE_NAME,
|
|||||||
"export_image_before_for_tags",
|
"export_image_before_for_tags",
|
||||||
"bool",
|
"bool",
|
||||||
_("photils: use exported image for tag request"),
|
_("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. " ..
|
_("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." ..
|
"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."),
|
"The embedded thumbnail could speedup the tag suggestion but can fail if the RAW file is not supported."),
|
||||||
true)
|
true)
|
||||||
|
|
||||||
dt.register_event("photils", "mouse-over-image-changed",
|
dt.register_event("photils", "mouse-over-image-changed",
|
||||||
|
|||||||
@@ -49,23 +49,10 @@ local debug = require "darktable.debug"
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", "quicktag")
|
du.check_min_api_version("7.0.0", "quicktag")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("quick tag"),
|
|
||||||
purpose = _("use buttons to quickly apply tags assigned to them"),
|
|
||||||
author = "Christian Kanzian",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/quicktag"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -75,6 +62,15 @@ qt.module_installed = false
|
|||||||
qt.event_registered = false
|
qt.event_registered = false
|
||||||
qt.widget_table = {}
|
qt.widget_table = {}
|
||||||
|
|
||||||
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("quicktag",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("quicktag", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
-- maximum length of button labels
|
-- maximum length of button labels
|
||||||
dt.preferences.register("quickTag",
|
dt.preferences.register("quickTag",
|
||||||
"labellength",
|
"labellength",
|
||||||
@@ -197,7 +193,7 @@ local function install_module()
|
|||||||
if not qt.module_installed then
|
if not qt.module_installed then
|
||||||
dt.register_lib(
|
dt.register_lib(
|
||||||
"quicktag", -- Module name
|
"quicktag", -- Module name
|
||||||
_("quick tag"), -- name
|
"quicktag", -- name
|
||||||
true, -- expandable
|
true, -- expandable
|
||||||
false, -- resetable
|
false, -- resetable
|
||||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 490}},
|
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 490}},
|
||||||
@@ -304,7 +300,7 @@ end
|
|||||||
for i=1,qnr do
|
for i=1,qnr do
|
||||||
dt.register_event("quicktag " .. tostring(i), "shortcut",
|
dt.register_event("quicktag " .. tostring(i), "shortcut",
|
||||||
function(event, shortcut) tagattach(tostring(quicktag_table[i])) end,
|
function(event, shortcut) tagattach(tostring(quicktag_table[i])) end,
|
||||||
string.format(_("quick tag %i"),i))
|
string.format(_("quicktag %i"),i))
|
||||||
end
|
end
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|||||||
@@ -44,23 +44,10 @@ local du = require "lib/dtutils"
|
|||||||
-- added version check
|
-- added version check
|
||||||
du.check_min_api_version("7.0.0", "rate_group")
|
du.check_min_api_version("7.0.0", "rate_group")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("rate group"),
|
|
||||||
purpose = _("rate all images in a group"),
|
|
||||||
author = "Dom H (dom@hxy.io)",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/rate_group"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -75,9 +62,9 @@ local function apply_rating(rating)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if rating < 0 then
|
if rating < 0 then
|
||||||
dt.print(_("rejecting group(s)"))
|
dt.print("rejecting group(s)")
|
||||||
else
|
else
|
||||||
dt.print(string.format(_("applying rating %d to group(s)"), rating))
|
dt.print("applying rating " ..rating.. " to group(s)")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -94,38 +81,37 @@ end
|
|||||||
dt.register_event("rg_reject", "shortcut",
|
dt.register_event("rg_reject", "shortcut",
|
||||||
function(event, shortcut)
|
function(event, shortcut)
|
||||||
apply_rating(-1)
|
apply_rating(-1)
|
||||||
end, _("reject group"))
|
end, "Reject group")
|
||||||
|
|
||||||
dt.register_event("rg0", "shortcut",
|
dt.register_event("rg0", "shortcut",
|
||||||
function(event, shortcut)
|
function(event, shortcut)
|
||||||
apply_rating(0)
|
apply_rating(0)
|
||||||
end, string.format(_("rate group %d"), 0)
|
end, "Rate group 0")
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event("rg1", "shortcut",
|
dt.register_event("rg1", "shortcut",
|
||||||
function(event, shortcut)
|
function(event, shortcut)
|
||||||
apply_rating(1)
|
apply_rating(1)
|
||||||
end, string.format(_("rate group %d"), 1))
|
end, "Rate group 1")
|
||||||
|
|
||||||
dt.register_event("rg2", "shortcut",
|
dt.register_event("rg2", "shortcut",
|
||||||
function(event, shortcut)
|
function(event, shortcut)
|
||||||
apply_rating(2)
|
apply_rating(2)
|
||||||
end, string.format(_("rate group %d"), 2))
|
end, "Rate group 2")
|
||||||
|
|
||||||
dt.register_event("rg3", "shortcut",
|
dt.register_event("rg3", "shortcut",
|
||||||
function(event, shortcut)
|
function(event, shortcut)
|
||||||
apply_rating(3)
|
apply_rating(3)
|
||||||
end, string.format(_("rate group %d"), 3))
|
end, "Rate group 3")
|
||||||
|
|
||||||
dt.register_event("rg4", "shortcut",
|
dt.register_event("rg4", "shortcut",
|
||||||
function(event, shortcut)
|
function(event, shortcut)
|
||||||
apply_rating(4)
|
apply_rating(4)
|
||||||
end, string.format(_("rate group %d"), 4))
|
end, "Rate group 4")
|
||||||
|
|
||||||
dt.register_event("rg5", "shortcut",
|
dt.register_event("rg5", "shortcut",
|
||||||
function(event, shortcut)
|
function(event, shortcut)
|
||||||
apply_rating(5)
|
apply_rating(5)
|
||||||
end, string.format(_("rate group %d"), 5))
|
end, "Rate group 5")
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
|
|||||||
@@ -36,23 +36,10 @@ local debug = require "darktable.debug"
|
|||||||
du.check_min_api_version("7.0.0", "rename-tags")
|
du.check_min_api_version("7.0.0", "rename-tags")
|
||||||
du.deprecated("contrib/rename-tags.lua","darktable release 4.0")
|
du.deprecated("contrib/rename-tags.lua","darktable release 4.0")
|
||||||
|
|
||||||
local gettext = darktable.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("rename tags"),
|
|
||||||
purpose = _("rename an existing tag"),
|
|
||||||
author = "Sebastian Witt (se.witt@gmx.net)",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/rename-tags"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -63,8 +50,8 @@ rt.module_installed = false
|
|||||||
rt.event_registered = false
|
rt.event_registered = false
|
||||||
|
|
||||||
-- GUI entries
|
-- GUI entries
|
||||||
local old_tag = darktable.new_widget("entry") { tooltip = _("enter old tag") }
|
local old_tag = darktable.new_widget("entry") { tooltip = "Enter old tag" }
|
||||||
local new_tag = darktable.new_widget("entry") { tooltip = _("enter new tag") }
|
local new_tag = darktable.new_widget("entry") { tooltip = "Enter new tag" }
|
||||||
|
|
||||||
local function rename_reset()
|
local function rename_reset()
|
||||||
old_tag.text = ''
|
old_tag.text = ''
|
||||||
@@ -75,11 +62,11 @@ end
|
|||||||
local function rename_tags()
|
local function rename_tags()
|
||||||
-- If entries are empty, return
|
-- If entries are empty, return
|
||||||
if old_tag.text == '' then
|
if old_tag.text == '' then
|
||||||
darktable.print (_("old tag can't be empty"))
|
darktable.print ("Old tag can't be empty")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if new_tag.text == '' then
|
if new_tag.text == '' then
|
||||||
darktable.print (_("new tag can't be empty"))
|
darktable.print ("New tag can't be empty")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -89,12 +76,12 @@ local function rename_tags()
|
|||||||
local ot = darktable.tags.find (old_tag.text)
|
local ot = darktable.tags.find (old_tag.text)
|
||||||
|
|
||||||
if not ot then
|
if not ot then
|
||||||
darktable.print (_("old tag does not exist"))
|
darktable.print ("Old tag does not exist")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Show job
|
-- Show job
|
||||||
local job = darktable.gui.create_job (_("renaming tag"), true)
|
local job = darktable.gui.create_job ("Renaming tag", true)
|
||||||
|
|
||||||
old_tag.editable = false
|
old_tag.editable = false
|
||||||
new_tag.editable = false
|
new_tag.editable = false
|
||||||
@@ -117,7 +104,7 @@ local function rename_tags()
|
|||||||
darktable.tags.delete (ot)
|
darktable.tags.delete (ot)
|
||||||
|
|
||||||
job.valid = false
|
job.valid = false
|
||||||
darktable.print (string.format(_("renamed tags for %d images"), count))
|
darktable.print ("Renamed tags for " .. count .. " images")
|
||||||
old_tag.editable = true
|
old_tag.editable = true
|
||||||
new_tag.editable = true
|
new_tag.editable = true
|
||||||
|
|
||||||
@@ -128,7 +115,7 @@ end
|
|||||||
|
|
||||||
local function install_module()
|
local function install_module()
|
||||||
if not rt.module_installed then
|
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)
|
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
|
rt.module_installed = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -144,13 +131,13 @@ end
|
|||||||
-- GUI
|
-- GUI
|
||||||
local old_widget = darktable.new_widget ("box") {
|
local old_widget = darktable.new_widget ("box") {
|
||||||
orientation = "horizontal",
|
orientation = "horizontal",
|
||||||
darktable.new_widget("label") { label = _("old tag") },
|
darktable.new_widget("label") { label = "Old tag" },
|
||||||
old_tag
|
old_tag
|
||||||
}
|
}
|
||||||
|
|
||||||
local new_widget = darktable.new_widget ("box") {
|
local new_widget = darktable.new_widget ("box") {
|
||||||
orientation = "horizontal",
|
orientation = "horizontal",
|
||||||
darktable.new_widget("label") { label = _("new tag") },
|
darktable.new_widget("label") { label = "New tag" },
|
||||||
new_tag
|
new_tag
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +146,7 @@ rt.rename_widget = darktable.new_widget ("box") {
|
|||||||
reset_callback = rename_reset,
|
reset_callback = rename_reset,
|
||||||
old_widget,
|
old_widget,
|
||||||
new_widget,
|
new_widget,
|
||||||
darktable.new_widget("button") { label = _("go"), clicked_callback = rename_tags }
|
darktable.new_widget("button") { label = "Go", clicked_callback = rename_tags }
|
||||||
}
|
}
|
||||||
|
|
||||||
if darktable.gui.current_view().id == "lighttable" then
|
if darktable.gui.current_view().id == "lighttable" then
|
||||||
|
|||||||
@@ -43,19 +43,27 @@
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local ds = require "lib/dtutils.string"
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "rename_images")
|
du.check_min_api_version("7.0.0", "rename_images")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("rename_images",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext("rename_images", msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- namespace variable
|
-- namespace variable
|
||||||
local rename = {
|
local rename = {
|
||||||
presets = {},
|
presets = {},
|
||||||
|
substitutes = {},
|
||||||
|
placeholders = {"ROLL_NAME","FILE_FOLDER","FILE_NAME","FILE_EXTENSION","ID","VERSION","SEQUENCE","YEAR","MONTH","DAY",
|
||||||
|
"HOUR","MINUTE","SECOND","EXIF_YEAR","EXIF_MONTH","EXIF_DAY","EXIF_HOUR","EXIF_MINUTE","EXIF_SECOND",
|
||||||
|
"STARS","LABELS","MAKER","MODEL","TITLE","CREATOR","PUBLISHER","RIGHTS","USERNAME","PICTURES_FOLDER",
|
||||||
|
"HOME","DESKTOP","EXIF_ISO","EXIF_EXPOSURE","EXIF_EXPOSURE_BIAS","EXIF_APERTURE","EXIF_FOCUS_DISTANCE",
|
||||||
|
"EXIF_FOCAL_LENGTH","LONGITUDE","LATITUDE","ELEVATION","LENS","DESCRIPTION","EXIF_CROP"},
|
||||||
widgets = {},
|
widgets = {},
|
||||||
}
|
}
|
||||||
rename.module_installed = false
|
rename.module_installed = false
|
||||||
@@ -64,13 +72,6 @@ rename.event_registered = false
|
|||||||
-- script_manager integration
|
-- script_manager integration
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("rename images"),
|
|
||||||
purpose = _("rename an image file or files"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/rename_images"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -85,13 +86,90 @@ local MODULE_NAME = "rename_images"
|
|||||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
||||||
local USER = os.getenv("USERNAME")
|
local USER = os.getenv("USERNAME")
|
||||||
local HOME = os.getenv(dt.configuration.running_os == "windows" and "HOMEPATH" or "HOME")
|
local HOME = os.getenv(dt.configuration.running_os == "windows" and "HOMEPATH" or "HOME")
|
||||||
local PICTURES = HOME .. PS .. dt.configuration.running_os == "windows" and _("My Pictures") or _("Pictures")
|
local PICTURES = HOME .. PS .. dt.configuration.running_os == "windows" and "My Pictures" or "Pictures"
|
||||||
local DESKTOP = HOME .. PS .. "Desktop"
|
local DESKTOP = HOME .. PS .. "Desktop"
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- F U N C T I O N S
|
-- F U N C T I O N S
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
local function build_substitution_list(image, sequence, datetime, username, pic_folder, home, desktop)
|
||||||
|
-- build the argument substitution list from each image
|
||||||
|
-- local datetime = os.date("*t")
|
||||||
|
local colorlabels = {}
|
||||||
|
if image.red then table.insert(colorlabels, "red") end
|
||||||
|
if image.yellow then table.insert(colorlabels, "yellow") end
|
||||||
|
if image.green then table.insert(colorlabels, "green") end
|
||||||
|
if image.blue then table.insert(colorlabels, "blue") end
|
||||||
|
if image.purple then table.insert(colorlabels, "purple") end
|
||||||
|
local labels = #colorlabels == 1 and colorlabels[1] or du.join(colorlabels, ",")
|
||||||
|
local eyear,emon,eday,ehour,emin,esec = string.match(image.exif_datetime_taken, "(%d-):(%d-):(%d-) (%d-):(%d-):(%d-)$")
|
||||||
|
local replacements = {image.film,
|
||||||
|
image.path,
|
||||||
|
df.get_filename(image.filename),
|
||||||
|
string.upper(df.get_filetype(image.filename)),
|
||||||
|
image.id,image.duplicate_index,
|
||||||
|
string.format("%04d", sequence),
|
||||||
|
datetime.year,
|
||||||
|
string.format("%02d", datetime.month),
|
||||||
|
string.format("%02d", datetime.day),
|
||||||
|
string.format("%02d", datetime.hour),
|
||||||
|
string.format("%02d", datetime.min),
|
||||||
|
string.format("%02d", datetime.sec),
|
||||||
|
eyear,
|
||||||
|
emon,
|
||||||
|
eday,
|
||||||
|
ehour,
|
||||||
|
emin,
|
||||||
|
esec,
|
||||||
|
image.rating,
|
||||||
|
labels,
|
||||||
|
image.exif_maker,
|
||||||
|
image.exif_model,
|
||||||
|
image.title,
|
||||||
|
image.creator,
|
||||||
|
image.publisher,
|
||||||
|
image.rights,
|
||||||
|
username,
|
||||||
|
pic_folder,
|
||||||
|
home,
|
||||||
|
desktop,
|
||||||
|
image.exif_iso,
|
||||||
|
image.exif_exposure,
|
||||||
|
image.exif_exposure_bias,
|
||||||
|
image.exif_aperture,
|
||||||
|
image.exif_focus_distance,
|
||||||
|
image.exif_focal_length,
|
||||||
|
image.longitude,
|
||||||
|
image.latitude,
|
||||||
|
image.elevation,
|
||||||
|
image.exif_lens,
|
||||||
|
image.description,
|
||||||
|
image.exif_crop
|
||||||
|
}
|
||||||
|
|
||||||
|
for i=1,#rename.placeholders,1 do rename.substitutes[rename.placeholders[i]] = replacements[i] end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function substitute_list(str)
|
||||||
|
-- replace the substitution variables in a string
|
||||||
|
for match in string.gmatch(str, "%$%(.-%)") do
|
||||||
|
local var = string.match(match, "%$%((.-)%)")
|
||||||
|
if rename.substitutes[var] then
|
||||||
|
str = string.gsub(str, "%$%("..var.."%)", rename.substitutes[var])
|
||||||
|
else
|
||||||
|
dt.print_error(_("unrecognized variable " .. var))
|
||||||
|
dt.print(_("unknown variable " .. var .. ", aborting..."))
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
local function clear_substitute_list()
|
||||||
|
for i=1,#rename.placeholders,1 do rename.substitutes[rename.placeholders[i]] = nil end
|
||||||
|
end
|
||||||
|
|
||||||
local function stop_job(job)
|
local function stop_job(job)
|
||||||
job.valid = false
|
job.valid = false
|
||||||
end
|
end
|
||||||
@@ -130,10 +208,9 @@ end
|
|||||||
|
|
||||||
local function do_rename(images)
|
local function do_rename(images)
|
||||||
if #images > 0 then
|
if #images > 0 then
|
||||||
local first_image = images[1]
|
|
||||||
local pattern = rename.widgets.pattern.text
|
local pattern = rename.widgets.pattern.text
|
||||||
dt.preferences.write(MODULE_NAME, "pattern", "string", pattern)
|
dt.preferences.write(MODULE_NAME, "pattern", "string", pattern)
|
||||||
dt.print_log("pattern is " .. pattern)
|
dt.print_log(_("pattern is " .. pattern))
|
||||||
if string.len(pattern) > 0 then
|
if string.len(pattern) > 0 then
|
||||||
local datetime = os.date("*t")
|
local datetime = os.date("*t")
|
||||||
|
|
||||||
@@ -141,14 +218,14 @@ local function do_rename(images)
|
|||||||
for i, image in ipairs(images) do
|
for i, image in ipairs(images) do
|
||||||
if job.valid then
|
if job.valid then
|
||||||
job.percent = i / #images
|
job.percent = i / #images
|
||||||
ds.build_substitute_list(image, i, pattern, USER, PICTURES, HOME, DESKTOP)
|
build_substitution_list(image, i, datetime, USER, PICTURES, HOME, DESKTOP)
|
||||||
local new_name = ds.substitute_list(pattern)
|
local new_name = substitute_list(pattern)
|
||||||
if new_name == -1 then
|
if new_name == -1 then
|
||||||
dt.print(_("unable to do variable substitution, exiting..."))
|
dt.print(_("unable to do variable substitution, exiting..."))
|
||||||
stop_job(job)
|
stop_job(job)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
ds.clear_substitute_list()
|
clear_substitute_list()
|
||||||
local args = {}
|
local args = {}
|
||||||
local path = string.sub(df.get_path(new_name), 1, -2)
|
local path = string.sub(df.get_path(new_name), 1, -2)
|
||||||
if string.len(path) == 0 then
|
if string.len(path) == 0 then
|
||||||
@@ -173,8 +250,7 @@ local function do_rename(images)
|
|||||||
stop_job(job)
|
stop_job(job)
|
||||||
local collect_rules = dt.gui.libs.collect.filter()
|
local collect_rules = dt.gui.libs.collect.filter()
|
||||||
dt.gui.libs.collect.filter(collect_rules)
|
dt.gui.libs.collect.filter(collect_rules)
|
||||||
dt.gui.views.lighttable.set_image_visible(first_image)
|
dt.print(_("renamed " .. #images .. " images"))
|
||||||
dt.print(string.format(_("renamed %d images"), #images))
|
|
||||||
else -- pattern length
|
else -- pattern length
|
||||||
dt.print_error("no pattern supplied, returning...")
|
dt.print_error("no pattern supplied, returning...")
|
||||||
dt.print(_("please enter the new name or pattern"))
|
dt.print(_("please enter the new name or pattern"))
|
||||||
@@ -185,19 +261,60 @@ local function do_rename(images)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function reset_callback()
|
||||||
|
rename.widgets.pattern.text = ""
|
||||||
|
end
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- W I D G E T S
|
-- W I D G E T S
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
rename.widgets.pattern = dt.new_widget("entry"){
|
rename.widgets.pattern = dt.new_widget("entry"){
|
||||||
tooltip = ds.get_substitution_tooltip(),
|
tooltip = _("$(ROLL_NAME) - film roll name\n") ..
|
||||||
placeholder = _("enter pattern") .. "$(FILE_FOLDER)/$(FILE_NAME)",
|
_("$(FILE_FOLDER) - image file folder\n") ..
|
||||||
text = "",
|
_("$(FILE_NAME) - image file name\n") ..
|
||||||
reset_callback = function(self)
|
_("$(FILE_EXTENSION) - image file extension\n") ..
|
||||||
self.text = ""
|
_("$(ID) - image id\n") ..
|
||||||
dt.preferences.write(MODULE_NAME, "pattern", "string", self.text)
|
_("$(VERSION) - version number\n") ..
|
||||||
end
|
_("$(SEQUENCE) - sequence number of selection\n") ..
|
||||||
|
_("$(YEAR) - current year\n") ..
|
||||||
|
_("$(MONTH) - current month\n") ..
|
||||||
|
_("$(DAY) - current day\n") ..
|
||||||
|
_("$(HOUR) - current hour\n") ..
|
||||||
|
_("$(MINUTE) - current minute\n") ..
|
||||||
|
_("$(SECOND) - current second\n") ..
|
||||||
|
_("$(EXIF_YEAR) - EXIF year\n") ..
|
||||||
|
_("$(EXIF_MONTH) - EXIF month\n") ..
|
||||||
|
_("$(EXIF_DAY) - EXIF day\n") ..
|
||||||
|
_("$(EXIF_HOUR) - EXIF hour\n") ..
|
||||||
|
_("$(EXIF_MINUTE) - EXIF minute\n") ..
|
||||||
|
_("$(EXIF_SECOND) - EXIF seconds\n") ..
|
||||||
|
_("$(EXIF_ISO) - EXIF ISO\n") ..
|
||||||
|
_("$(EXIF_EXPOSURE) - EXIF exposure\n") ..
|
||||||
|
_("$(EXIF_EXPOSURE_BIAS) - EXIF exposure bias\n") ..
|
||||||
|
_("$(EXIF_APERTURE) - EXIF aperture\n") ..
|
||||||
|
_("$(EXIF_FOCAL_LENGTH) - EXIF focal length\n") ..
|
||||||
|
_("$(EXIF_FOCUS_DISTANCE) - EXIF focus distance\n") ..
|
||||||
|
_("$(EXIF_CROP) - EXIF crop\n") ..
|
||||||
|
_("$(LONGITUDE) - longitude\n") ..
|
||||||
|
_("$(LATITUDE) - latitude\n") ..
|
||||||
|
_("$(ELEVATION) - elevation\n") ..
|
||||||
|
_("$(STARS) - star rating\n") ..
|
||||||
|
_("$(LABELS) - color labels\n") ..
|
||||||
|
_("$(MAKER) - camera maker\n") ..
|
||||||
|
_("$(MODEL) - camera model\n") ..
|
||||||
|
_("$(LENS) - lens\n") ..
|
||||||
|
_("$(TITLE) - title from metadata\n") ..
|
||||||
|
_("$(DESCRIPTION) - description from metadata\n") ..
|
||||||
|
_("$(CREATOR) - creator from metadata\n") ..
|
||||||
|
_("$(PUBLISHER) - publisher from metadata\n") ..
|
||||||
|
_("$(RIGHTS) - rights from metadata\n") ..
|
||||||
|
_("$(USERNAME) - username\n") ..
|
||||||
|
_("$(PICTURES_FOLDER) - pictures folder\n") ..
|
||||||
|
_("$(HOME) - user's home directory\n") ..
|
||||||
|
_("$(DESKTOP) - desktop directory"),
|
||||||
|
placeholder = _("enter pattern $(FILE_FOLDER)/$(FILE_NAME)"),
|
||||||
|
text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
local pattern_pref = dt.preferences.read(MODULE_NAME, "pattern", "string")
|
local pattern_pref = dt.preferences.read(MODULE_NAME, "pattern", "string")
|
||||||
|
|||||||
@@ -20,85 +20,46 @@ Enable selection of non-existing images in the the currently worked on images, e
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local ds = require "lib/dtutils.string"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
|
|
||||||
-- module name
|
-- module name
|
||||||
local MODULE <const> = "select_non_existing"
|
local MODULE = "select_non_existing"
|
||||||
|
|
||||||
du.check_min_api_version("9.1.0", MODULE)
|
du.check_min_api_version("9.1.0", MODULE)
|
||||||
|
|
||||||
-- figure out the path separator
|
-- figure out the path separator
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.info
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
local gettext = dt.gettext
|
||||||
|
gettext.bindtextdomain(MODULE, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext(MODULE, msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function stop_job(job)
|
local function stop_job(job)
|
||||||
job.valid = false
|
job.valid = false
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_file_list(dir)
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
local file_list = {}
|
|
||||||
local cmd = nil
|
|
||||||
log.msg(log.debug, "called get_file_list with " .. dir)
|
|
||||||
|
|
||||||
if dt.configuration.running_os == "windows" then
|
|
||||||
cmd = "forfiles /P " .. ds.sanitize(dir) .. " /M * /C \"cmd /c echo @file\""
|
|
||||||
else
|
|
||||||
cmd = "cd " .. ds.sanitize(dir) ..";ls -1"
|
|
||||||
end
|
|
||||||
log.msg(log.info, "cmd is " .. cmd)
|
|
||||||
|
|
||||||
local p = io.popen(cmd)
|
|
||||||
if p then
|
|
||||||
for line in p:lines() do
|
|
||||||
if line:len() > 4 then
|
|
||||||
if not string.match(line, "xmp$") and not string.match(line, "XMP$") then
|
|
||||||
line = line:gsub("\"", "")
|
|
||||||
file_list[line] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
p:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
return file_list
|
|
||||||
end
|
|
||||||
|
|
||||||
local function select_nonexisting_images(event, images)
|
local function select_nonexisting_images(event, images)
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
local selection = {}
|
||||||
local selection = {}
|
|
||||||
local known_files = {}
|
|
||||||
|
|
||||||
local job = dt.gui.create_job(_("select non existing images"), true, stop_job)
|
local job = dt.gui.create_job(_("select non existing images"), true, stop_job)
|
||||||
for key,image in ipairs(images) do
|
for key,image in ipairs(images) do
|
||||||
if(job.valid) then
|
if(job.valid) then
|
||||||
if not known_files[image.path] then
|
job.percent = (key - 1)/#images
|
||||||
known_files[image.path] = get_file_list(image.path)
|
local filepath = image.path..PS..image.filename
|
||||||
end
|
local file_exists = df.test_file(filepath, "e")
|
||||||
if key % 10 == 0 then
|
dt.print_log(filepath.." exists? => "..tostring(file_exists))
|
||||||
job.percent = key/#images
|
if (not file_exists) then
|
||||||
end
|
table.insert(selection, image)
|
||||||
local file_exists = known_files[image.path][image.filename]
|
end
|
||||||
log.msg(log.debug, image.path .. PS .. image.filename .." exists? => "..tostring(file_exists))
|
else
|
||||||
if (not file_exists) then
|
break
|
||||||
table.insert(selection, image)
|
end
|
||||||
end
|
end
|
||||||
else
|
stop_job(job)
|
||||||
break
|
|
||||||
end
|
return selection
|
||||||
end
|
|
||||||
stop_job(job)
|
|
||||||
|
|
||||||
return selection
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function destroy()
|
local function destroy()
|
||||||
@@ -112,13 +73,5 @@ dt.gui.libs.select.register_selection(
|
|||||||
_("select all non-existing images in the current images"))
|
_("select all non-existing images in the current images"))
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("select non existing"),
|
|
||||||
purpose = _("enable selection of non-existing images"),
|
|
||||||
author = "Dirk Dittmar",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/select_non_existing"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
return script_data
|
return script_data
|
||||||
@@ -20,30 +20,26 @@ Enable selection of untagged images (darktable|* tags are ignored)
|
|||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "select_untagged")
|
du.check_min_api_version("7.0.0", "select_untagged")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("select untagged"),
|
|
||||||
purpose = _("enable selection of untagged images"),
|
|
||||||
author = "Jannis_V",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/select_untagged"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("select_untagged",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("select_untagged", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local function stop_job(job)
|
local function stop_job(job)
|
||||||
job.valid = false
|
job.valid = false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,199 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
selected_image_visible.lua - make the selected image visible in lighttable
|
|
||||||
|
|
||||||
Copyright (C) 2026 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
selected_image_visible - make the selected image visible in lighttable
|
|
||||||
|
|
||||||
selected_image_visible adds a bshortcut to make the currently
|
|
||||||
selected image or the first image of a selection visible in lighttable
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
* enable the script in script_manager
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
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 dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("9.6.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("selected image visible"), -- visible name of script
|
|
||||||
purpose = _("make the selected image visible in lighttable"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/selected_image_visible/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "selected_image_visible"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.info
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local selected_image_visible = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
selected_image_visible.log_level = DEFAULT_LOG_LEVEL
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = selected_image_visible
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function set_image_visible()
|
|
||||||
if dt.gui.current_view().name == "lighttable" and #dt.gui.action_images > 0 then
|
|
||||||
dt.gui.views.lighttable.set_image_visible(dt.gui.action_images[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function set_button_sensitive()
|
|
||||||
if dt.gui.current_view().name == "lighttable"
|
|
||||||
and #dt.gui.selection() > 0
|
|
||||||
and not dt.gui.views.lighttable.is_image_visible(dt.gui.selection()[1]) then
|
|
||||||
dt.gui.libs.image.set_sensitive(MODULE, true)
|
|
||||||
else
|
|
||||||
dt.gui.libs.image.set_sensitive(MODULE, false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.gui.libs.image.register_action(
|
|
||||||
MODULE, _("selected image visible"),
|
|
||||||
function(event, images) set_image_visible() end,
|
|
||||||
_("make selected image visible in lighttable")
|
|
||||||
)
|
|
||||||
|
|
||||||
set_button_sensitive()
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
-- put things to destroy (events, storages, etc) here
|
|
||||||
dt.destroy_event(MODULE, "selection-changed")
|
|
||||||
dt.destroy_event(MODULE, "mouse-over-image-changed")
|
|
||||||
dt.gui.libs.image.destroy_action(MODULE)
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
if not dt.query_event(MODULE, "shortcut") then
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, shortcut)
|
|
||||||
set_image_visible()
|
|
||||||
end, _("make selected image visible in lighttable")
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "selection-changed",
|
|
||||||
function(event)
|
|
||||||
set_button_sensitive()
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "mouse-over-image-changed",
|
|
||||||
function(event, image)
|
|
||||||
set_button_sensitive()
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -27,30 +27,26 @@ USAGE
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "slideshowMusic")
|
du.check_min_api_version("7.0.0", "slideshowMusic")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("slideshow music"),
|
|
||||||
purpose = _("play music during a slideshow"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/slideshowMusic"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("slideshowMusic",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("slideshowMusic", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local function playSlideshowMusic(_, old_view, new_view)
|
local function playSlideshowMusic(_, old_view, new_view)
|
||||||
local filename, playMusic
|
local filename, playMusic
|
||||||
|
|
||||||
@@ -58,7 +54,7 @@ local function playSlideshowMusic(_, old_view, new_view)
|
|||||||
playMusic = dt.preferences.read("slideshowMusic","PlaySlideshowMusic","bool")
|
playMusic = dt.preferences.read("slideshowMusic","PlaySlideshowMusic","bool")
|
||||||
|
|
||||||
if not df.check_if_bin_exists("rhythmbox-client") then
|
if not df.check_if_bin_exists("rhythmbox-client") then
|
||||||
dt.print_error("rhythmbox-client not found")
|
dt.print_error(_("rhythmbox-client not found"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -74,7 +70,7 @@ local function playSlideshowMusic(_, old_view, new_view)
|
|||||||
if (old_view and old_view.id == "slideshow") then
|
if (old_view and old_view.id == "slideshow") then
|
||||||
stopCommand = "rhythmbox-client --pause"
|
stopCommand = "rhythmbox-client --pause"
|
||||||
--dt.print_error(stopCommand)
|
--dt.print_error(stopCommand)
|
||||||
dt.control.execute(stopCommand)
|
dt.control.execute( stopCommand)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -87,12 +83,12 @@ function destroy()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Preferences
|
-- Preferences
|
||||||
dt.preferences.register("slideshowMusic", "SlideshowMusic", "file", _("slideshow background music file"), "", "")
|
dt.preferences.register("slideshowMusic", "SlideshowMusic", "file", _("Slideshow background music file"), "", "")
|
||||||
dt.preferences.register("slideshowMusic",
|
dt.preferences.register("slideshowMusic",
|
||||||
"PlaySlideshowMusic",
|
"PlaySlideshowMusic",
|
||||||
"bool",
|
"bool",
|
||||||
_("play slideshow background music"),
|
_("Play slideshow background music"),
|
||||||
_("plays music with rhythmbox if a slideshow starts"),
|
_("Plays music with rhythmbox if a slideshow starts"),
|
||||||
true)
|
true)
|
||||||
-- Register
|
-- Register
|
||||||
dt.register_event("slideshow_music", "view-changed",
|
dt.register_event("slideshow_music", "view-changed",
|
||||||
|
|||||||
@@ -1,313 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
toggle_group_view.lua - toggle only group visible in lighttable
|
|
||||||
|
|
||||||
Copyright (C) 2024 Bill Ferguson <wpferguson.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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
toggle_group_view - toggle only group visible in lighttable
|
|
||||||
|
|
||||||
toggle_group_view provides a way to "separate" a group from
|
|
||||||
the lighttable display and limit the display to just the group.
|
|
||||||
Pressing the shortcut again returns to the full lighttable view.
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
* place the script in your lua scripts folder
|
|
||||||
* start the script with script manager
|
|
||||||
* assign a key sequence to the shortcut
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
Bill Ferguson <wpferguson.com>
|
|
||||||
|
|
||||||
CHANGES
|
|
||||||
]]
|
|
||||||
|
|
||||||
local dt = require "darktable"
|
|
||||||
local du = require "lib/dtutils"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "toggle_group_view"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.error
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
dt.gettext.bindtextdomain(MODULE , dt.configuration.config_dir .. "/lua/locale/")
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(MODULE, msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = "toggle_group_view", -- name of script
|
|
||||||
purpose = _("toggle only group visible in lighttable"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/toggle_group_view/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local toggle_group_view = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local group_active = false
|
|
||||||
local filmroll = ""
|
|
||||||
local collection_rules = {}
|
|
||||||
local hover_active = true
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function is_group_member(image)
|
|
||||||
if image then
|
|
||||||
if #image:get_group_members() > 1 then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_group_displayed()
|
|
||||||
local rules = dt.gui.libs.collect.filter()
|
|
||||||
if rules[1].item == "DT_COLLECTION_PROP_GROUP_ID" then
|
|
||||||
local rule = dt.gui.libs.collect.new_rule()
|
|
||||||
|
|
||||||
rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
|
||||||
rule.data = dt.collection[1].path
|
|
||||||
rule.item = "DT_COLLECTION_PROP_FILMROLL"
|
|
||||||
|
|
||||||
table.insert(collection_rules, rule)
|
|
||||||
group_active = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function set_button_sensitive(image)
|
|
||||||
local selected_images = dt.gui.selection()
|
|
||||||
local sensitive
|
|
||||||
|
|
||||||
|
|
||||||
if group_active then
|
|
||||||
dt.gui.libs.image.set_sensitive(MODULE, true)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if #selected_images > 0 then
|
|
||||||
if is_group_member(selected_images[1]) then
|
|
||||||
sensitive = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if image then
|
|
||||||
if is_group_member(image) then
|
|
||||||
sensitive = true
|
|
||||||
else
|
|
||||||
sensitive = false
|
|
||||||
end
|
|
||||||
else
|
|
||||||
sensitive = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
dt.gui.libs.image.set_sensitive(MODULE, sensitive)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function add_collection_rule(rule_string)
|
|
||||||
|
|
||||||
local rule = dt.gui.libs.collect.new_rule()
|
|
||||||
|
|
||||||
rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
|
||||||
rule.data = rule_string
|
|
||||||
rule.item = "DT_COLLECTION_PROP_FILENAME"
|
|
||||||
|
|
||||||
table.insert(collection_rules, rule)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function show_group(image)
|
|
||||||
|
|
||||||
local collection_rule_string = ""
|
|
||||||
|
|
||||||
local first = true
|
|
||||||
|
|
||||||
collection_rules = {}
|
|
||||||
|
|
||||||
local group_id = image.group_leader
|
|
||||||
|
|
||||||
local rule = dt.gui.libs.collect.new_rule()
|
|
||||||
|
|
||||||
rule.mode = "DT_LIB_COLLECT_MODE_AND"
|
|
||||||
rule.data = tostring(group_id.id)
|
|
||||||
rule.item = "DT_COLLECTION_PROP_GROUP_ID"
|
|
||||||
|
|
||||||
table.insert(collection_rules, rule)
|
|
||||||
|
|
||||||
-- dt.gui.libs.collect.filter returns the previous rule set when you install another
|
|
||||||
collection_rules = dt.gui.libs.collect.filter(collection_rules)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function reset(image)
|
|
||||||
dt.gui.libs.collect.filter(collection_rules)
|
|
||||||
if image then
|
|
||||||
dt.gui.views.lighttable.set_image_visible(image)
|
|
||||||
else
|
|
||||||
log.msg(log.screen, _("no image supplied"))
|
|
||||||
end
|
|
||||||
set_button_sensitive(image)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- check if we are starting with a group already displayed and
|
|
||||||
-- configure appropriately
|
|
||||||
|
|
||||||
if not dt.preferences.read("darktable", "plugins/lighttable/act_on", "bool") then
|
|
||||||
hover_active = false
|
|
||||||
end
|
|
||||||
|
|
||||||
is_group_displayed()
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.gui.libs.image.register_action(
|
|
||||||
MODULE, "toggle group view",
|
|
||||||
function(event, images)
|
|
||||||
if group_active then
|
|
||||||
local image
|
|
||||||
if dt.gui.action_images[1] then
|
|
||||||
image = dt.gui.action_images[1]
|
|
||||||
reset(dt.gui.action_images[1])
|
|
||||||
else
|
|
||||||
image = dt.collection[1]
|
|
||||||
reset(dt.collection[1])
|
|
||||||
end
|
|
||||||
group_active = false
|
|
||||||
set_button_sensitive(image)
|
|
||||||
else
|
|
||||||
show_group(dt.gui.action_images[1])
|
|
||||||
group_active = true
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
_("toggle group view for selected image")
|
|
||||||
)
|
|
||||||
|
|
||||||
set_button_sensitive(nil)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
if hover_active then
|
|
||||||
dt.destroy_event(MODULE, "mouse-over-image-changed")
|
|
||||||
end
|
|
||||||
dt.destroy_event(MODULE, "selection_changed")
|
|
||||||
dt.gui.libs.image.destroy_action(MODULE, "toggle group view")
|
|
||||||
-- leave the shortcut so it doesn't need to be reassigned
|
|
||||||
-- the next time this module starts
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.register_event(
|
|
||||||
MODULE, "shortcut",
|
|
||||||
function(event, shortcut)
|
|
||||||
if #dt.gui.action_images > 0 then
|
|
||||||
if group_active then
|
|
||||||
reset(dt.gui.action_images[1])
|
|
||||||
group_active = false
|
|
||||||
else
|
|
||||||
show_group(dt.gui.action_images[1])
|
|
||||||
group_active = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
_("toggle group view for selected image")
|
|
||||||
)
|
|
||||||
|
|
||||||
---[[
|
|
||||||
if hover_active then
|
|
||||||
dt.register_event(MODULE, "mouse-over-image-changed",
|
|
||||||
function(event, image)
|
|
||||||
set_button_sensitive(image)
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
--]]
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "selection-changed",
|
|
||||||
function(event)
|
|
||||||
set_button_sensitive(dt.gui.action_images[1])
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -76,26 +76,14 @@ local darktable = require("darktable")
|
|||||||
local dtutils = require("lib/dtutils")
|
local dtutils = require("lib/dtutils")
|
||||||
local dtutils_file = require("lib/dtutils.file")
|
local dtutils_file = require("lib/dtutils.file")
|
||||||
local dtutils_system = require("lib/dtutils.system")
|
local dtutils_system = require("lib/dtutils.system")
|
||||||
local gettext = darktable.gettext.gettext
|
|
||||||
|
|
||||||
local LIB_ID = "transfer_hierarchy"
|
local LIB_ID = "transfer_hierarchy"
|
||||||
dtutils.check_min_api_version("7.0.0", LIB_ID)
|
dtutils.check_min_api_version("7.0.0", LIB_ID)
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("transfer hierarchy"),
|
|
||||||
purpose = _("allows the moving or copying of images from one directory tree to another, while preserving the existing hierarchy"),
|
|
||||||
author = "August Schwerdfeger (august@schwerdfeger.name)",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/transfer_hierarchy"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -108,6 +96,12 @@ local PATH_SEGMENT_REGEX = "(" .. PATH_SEPARATOR .. "?)([^" .. PATH_SEPARATOR ..
|
|||||||
unpack = unpack or table.unpack
|
unpack = unpack or table.unpack
|
||||||
gmatch = string.gfind or string.gmatch
|
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
|
-- Header material: END
|
||||||
|
|
||||||
|
|
||||||
@@ -144,7 +138,7 @@ end
|
|||||||
local function install_module()
|
local function install_module()
|
||||||
if not th.module_installed then
|
if not th.module_installed then
|
||||||
darktable.register_lib(LIB_ID,
|
darktable.register_lib(LIB_ID,
|
||||||
_("transfer hierarchy"), true, true, {
|
"transfer hierarchy", true, true, {
|
||||||
[darktable.gui.views.lighttable] = { "DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 700 }
|
[darktable.gui.views.lighttable] = { "DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 700 }
|
||||||
}, th.transfer_widget, nil, nil)
|
}, th.transfer_widget, nil, nil)
|
||||||
th.module_installed = true
|
th.module_installed = true
|
||||||
@@ -157,7 +151,7 @@ end
|
|||||||
-- Widgets and business logic: BEGIN
|
-- Widgets and business logic: BEGIN
|
||||||
|
|
||||||
local sourceTextBox = darktable.new_widget("entry") {
|
local sourceTextBox = darktable.new_widget("entry") {
|
||||||
tooltip = _("lowest directory containing all selected images"),
|
tooltip = _("Lowest directory containing all selected images"),
|
||||||
editable = false
|
editable = false
|
||||||
}
|
}
|
||||||
sourceTextBox.reset_callback = function() sourceTextBox.text = "" end
|
sourceTextBox.reset_callback = function() sourceTextBox.text = "" end
|
||||||
@@ -270,21 +264,21 @@ local function doTransfer(transferFunc)
|
|||||||
local sourceBase = sourceTextBox.text
|
local sourceBase = sourceTextBox.text
|
||||||
local destBase = destinationTextBox.text
|
local destBase = destinationTextBox.text
|
||||||
local destFilms = {}
|
local destFilms = {}
|
||||||
for film, i in pairs(films) do
|
for film, _ in pairs(films) do
|
||||||
films[film] = destBase .. string.sub(film.path, #sourceBase+1)
|
films[film] = destBase .. string.sub(film.path, #sourceBase+1)
|
||||||
if not pathExists(films[film]) then
|
if not pathExists(films[film]) then
|
||||||
if createDirectory(films[film]) == nil then
|
if createDirectory(films[film]) == nil then
|
||||||
darktable.print(string.format(_("transfer hierarchy: ERROR: could not create directory: %s"), films[film]))
|
darktable.print(_("transfer hierarchy: ERROR: could not create directory: " .. films[film]))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not pathIsDirectory(films[film]) then
|
if not pathIsDirectory(films[film]) then
|
||||||
darktable.print(string.format(_("transfer hierarchy: ERROR: not a directory: %s"), films[film]))
|
darktable.print(_("transfer hierarchy: ERROR: not a directory: " .. films[film]))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
destFilms[film] = darktable.films.new(films[film])
|
destFilms[film] = darktable.films.new(films[film])
|
||||||
if destFilms[film] == nil then
|
if destFilms[film] == nil then
|
||||||
darktable.print(string.format(_("transfer hierarchy: ERROR: could not create film: %s"), film.path))
|
darktable.print(_("transfer hierarchy: ERROR: could not create film: " .. film.path))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -293,7 +287,7 @@ local function doTransfer(transferFunc)
|
|||||||
srcFilms[img] = img.film
|
srcFilms[img] = img.film
|
||||||
end
|
end
|
||||||
|
|
||||||
local job = darktable.gui.create_job(string.format(_("transfer hierarchy (%d image%s)"), #(darktable.gui.action_images), (#(darktable.gui.action_images) == 1 and "" or "s")), true, stopTransfer)
|
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
|
job.percent = 0.0
|
||||||
local pctIncrement = 1.0 / #(darktable.gui.action_images)
|
local pctIncrement = 1.0 / #(darktable.gui.action_images)
|
||||||
for _,img in ipairs(darktable.gui.action_images) do
|
for _,img in ipairs(darktable.gui.action_images) do
|
||||||
@@ -351,7 +345,7 @@ th.transfer_widget = darktable.new_widget("box") {
|
|||||||
},
|
},
|
||||||
darktable.new_widget("button") {
|
darktable.new_widget("button") {
|
||||||
label = _("copy"),
|
label = _("copy"),
|
||||||
tooltip = _("copy all selected images"),
|
tooltip = _("Copy all selected images"),
|
||||||
clicked_callback = doCopy
|
clicked_callback = doCopy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,4 +399,4 @@ script_data.restart = restart
|
|||||||
script_data.destroy_method = "hide"
|
script_data.destroy_method = "hide"
|
||||||
script_data.show = restart
|
script_data.show = restart
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
1066
contrib/ultrahdr.lua
1066
contrib/ultrahdr.lua
File diff suppressed because it is too large
Load Diff
@@ -37,25 +37,14 @@ local dt = require "darktable"
|
|||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local df = require "lib/dtutils.file"
|
local df = require "lib/dtutils.file"
|
||||||
local dsys = require "lib/dtutils.system"
|
local dsys = require "lib/dtutils.system"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "video_ffmpeg")
|
du.check_min_api_version("7.0.0", "video_ffmpeg")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("video ffmpeg"),
|
|
||||||
purpose = _("timelapse video plugin based on ffmpeg"),
|
|
||||||
author = "Dominik Markiewicz",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contib/video_ffmpeg"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -64,6 +53,12 @@ script_data.show = nil -- only required for libs since the destroy_method only h
|
|||||||
local MODULE_NAME = "video_ffmpeg"
|
local MODULE_NAME = "video_ffmpeg"
|
||||||
|
|
||||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
||||||
|
gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext(MODULE_NAME, msgid)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
---- DECLARATIONS
|
---- DECLARATIONS
|
||||||
|
|
||||||
@@ -246,8 +241,8 @@ local function string_pref_write(name, widget_attribute)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local framerates_selector = dt.new_widget("combobox"){
|
local framerates_selector = dt.new_widget("combobox"){
|
||||||
label = _("frame rate"),
|
label = _("framerate"),
|
||||||
tooltip = _("select frame rate of output video"),
|
tooltip = _("select framerate of output video"),
|
||||||
value = combobox_pref_read("framerate", framerates),
|
value = combobox_pref_read("framerate", framerates),
|
||||||
changed_callback = combobox_pref_write("framerate"),
|
changed_callback = combobox_pref_write("framerate"),
|
||||||
table.unpack(framerates)
|
table.unpack(framerates)
|
||||||
@@ -299,7 +294,7 @@ else
|
|||||||
end
|
end
|
||||||
|
|
||||||
local output_directory_chooser = dt.new_widget("file_chooser_button"){
|
local output_directory_chooser = dt.new_widget("file_chooser_button"){
|
||||||
title = _("select export path"),
|
title = _("Select export path"),
|
||||||
is_directory = true,
|
is_directory = true,
|
||||||
tooltip =_("select the target directory for the timelapse. \nthe filename is created automatically."),
|
tooltip =_("select the target directory for the timelapse. \nthe filename is created automatically."),
|
||||||
value = string_pref_read("export_path", defaultVideoDir),
|
value = string_pref_read("export_path", defaultVideoDir),
|
||||||
@@ -380,7 +375,7 @@ local module_widget = dt.new_widget("box") {
|
|||||||
---- EXPORT & REGISTRATION
|
---- EXPORT & REGISTRATION
|
||||||
|
|
||||||
local function show_status(enf_storage, image, format, filename, number, total, high_quality, extra_data)
|
local function show_status(enf_storage, image, format, filename, number, total, high_quality, extra_data)
|
||||||
dt.print(string.format(_("export %d / %d", number), total))
|
dt.print(_("export ")..tostring(number).." / "..tostring(total))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function init_export(storage, img_format, images, high_quality, extra_data)
|
local function init_export(storage, img_format, images, high_quality, extra_data)
|
||||||
@@ -416,7 +411,7 @@ local function export(extra_data)
|
|||||||
local ffmpeg_path = df.check_if_bin_exists("ffmpeg")
|
local ffmpeg_path = df.check_if_bin_exists("ffmpeg")
|
||||||
if not ffmpeg_path then
|
if not ffmpeg_path then
|
||||||
dt.print_error("ffmpeg not found")
|
dt.print_error("ffmpeg not found")
|
||||||
dt.print(_("ERROR - ffmpeg not found"))
|
dt.print("ERROR - ffmpeg not found")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local dir = extra_data["tmp_dir"]
|
local dir = extra_data["tmp_dir"]
|
||||||
@@ -448,7 +443,7 @@ local function finalize_export(storage, images_table, extra_data)
|
|||||||
dt.print_error(filename, file.filename)
|
dt.print_error(filename, file.filename)
|
||||||
df.file_move(filename, tmp_dir .. PS .. i .. extra_data["img_ext"])
|
df.file_move(filename, tmp_dir .. PS .. i .. extra_data["img_ext"])
|
||||||
end
|
end
|
||||||
dt.print(_("start video building..."))
|
dt.print("Start video building...")
|
||||||
local result, path = export(extra_data)
|
local result, path = export(extra_data)
|
||||||
if result ~= 0 then
|
if result ~= 0 then
|
||||||
dt.print(_("ERROR: cannot build image, see console for more info"))
|
dt.print(_("ERROR: cannot build image, see console for more info"))
|
||||||
@@ -470,7 +465,7 @@ end
|
|||||||
|
|
||||||
dt.register_storage(
|
dt.register_storage(
|
||||||
"module_video_ffmpeg",
|
"module_video_ffmpeg",
|
||||||
_("video ffmpeg"),
|
_(MODULE_NAME),
|
||||||
show_status,
|
show_status,
|
||||||
finalize_export,
|
finalize_export,
|
||||||
nil,
|
nil,
|
||||||
|
|||||||
@@ -1,294 +0,0 @@
|
|||||||
--[[Export module to create a web gallery from selected images
|
|
||||||
|
|
||||||
copyright (c) 2025 Tino Mettler
|
|
||||||
|
|
||||||
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 this software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
]]
|
|
||||||
|
|
||||||
--[[
|
|
||||||
TODO:
|
|
||||||
- Lua: remove images dir if already existent
|
|
||||||
- Lua: translations
|
|
||||||
]]
|
|
||||||
|
|
||||||
local dt = require "darktable"
|
|
||||||
local df = require "lib/dtutils.file"
|
|
||||||
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
local temp = dt.preferences.read('web_gallery', 'title', 'string')
|
|
||||||
if temp == nil then temp = 'Darktable gallery' end
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return dt.gettext.gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
local title_widget = dt.new_widget("entry")
|
|
||||||
{
|
|
||||||
text = temp
|
|
||||||
}
|
|
||||||
|
|
||||||
local temp = dt.preferences.read('web_gallery', 'destination_dir', 'string')
|
|
||||||
if temp == nil then temp = '' end
|
|
||||||
|
|
||||||
local dest_dir_widget = dt.new_widget("file_chooser_button")
|
|
||||||
{
|
|
||||||
title = _("select output folder"),
|
|
||||||
tooltip = _("select output folder"),
|
|
||||||
value = temp,
|
|
||||||
is_directory = true,
|
|
||||||
changed_callback = function(this) dt.preferences.write('web_gallery', 'destination_dir', 'string', this.value) end
|
|
||||||
}
|
|
||||||
|
|
||||||
local gallery_widget = dt.new_widget("box")
|
|
||||||
{
|
|
||||||
orientation=vertical,
|
|
||||||
dt.new_widget("label"){label = _("gallery title")},
|
|
||||||
title_widget,
|
|
||||||
dt.new_widget("label"){label = _("destination directory")},
|
|
||||||
dest_dir_widget
|
|
||||||
}
|
|
||||||
|
|
||||||
local function get_file_name(file)
|
|
||||||
return file:match("[^" .. PS .. "]*.$")
|
|
||||||
end
|
|
||||||
|
|
||||||
function escape_js_string(str)
|
|
||||||
local replacements = {
|
|
||||||
['\\'] = '\\\\',
|
|
||||||
['"'] = '\\"',
|
|
||||||
["'"] = "\\'",
|
|
||||||
['\n'] = '\\n',
|
|
||||||
['\r'] = '\\r',
|
|
||||||
['\t'] = '\\t',
|
|
||||||
['\b'] = '\\b',
|
|
||||||
['\f'] = '\\f',
|
|
||||||
['\v'] = '\\v'
|
|
||||||
}
|
|
||||||
return (str:gsub('[\\\"\n\r\t\b\f\v\']', replacements))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function export_thumbnail(image, filename)
|
|
||||||
exporter = dt.new_format("jpeg")
|
|
||||||
exporter.quality = 90
|
|
||||||
exporter.max_height = 512
|
|
||||||
exporter.max_width = 512
|
|
||||||
exporter:write_image(image, filename, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function write_image(image, dest_dir, filename)
|
|
||||||
df.file_move(filename, dest_dir.. PS .. "images" .. PS .. get_file_name(filename))
|
|
||||||
export_thumbnail(image, dest_dir .. PS .. "thumbnails" .. PS .. "thumb_" .. get_file_name(filename))
|
|
||||||
end
|
|
||||||
|
|
||||||
function exiftool_get_image_dimensions(filename)
|
|
||||||
local handle = io.popen("exiftool " .. filename)
|
|
||||||
local result = handle:read("*a")
|
|
||||||
handle:close()
|
|
||||||
for line in result:gmatch("[^\r\n]+") do
|
|
||||||
local w = line:match("^Image Width%s*:%s*(%d+)")
|
|
||||||
if w then
|
|
||||||
width = tonumber(w)
|
|
||||||
end
|
|
||||||
local h = line:match("^Image Height%s*:%s*(%d+)")
|
|
||||||
if h then
|
|
||||||
height = tonumber(h)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if width and height then
|
|
||||||
return width, height
|
|
||||||
else
|
|
||||||
return nil, nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function stop_job(job)
|
|
||||||
job.valid = false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function fill_gallery_table(images_ordered, images_table, title, dest_dir, sizes, exiftool)
|
|
||||||
local gallery_data = { name = escape_js_string(title) }
|
|
||||||
|
|
||||||
local images = {}
|
|
||||||
local index = 1
|
|
||||||
local job = dt.gui.create_job(_("exporting thumbnail images"), true, stop_job)
|
|
||||||
|
|
||||||
for i, image in pairs(images_ordered) do
|
|
||||||
local filename = df.sanitize_filename(images_table[image])
|
|
||||||
dt.print(_("export thumbnail image ") .. index .. "/" .. #images_ordered)
|
|
||||||
write_image(image, dest_dir, filename)
|
|
||||||
|
|
||||||
if exiftool then
|
|
||||||
width, height = exiftool_get_image_dimensions(df.sanitize_filename(dest_dir .. PS .. "images" .. PS .. get_file_name(filename)))
|
|
||||||
else
|
|
||||||
width = sizes[index].width
|
|
||||||
height = sizes[index].height
|
|
||||||
end
|
|
||||||
|
|
||||||
local entry = { filename = "images" .. PS .. get_file_name(filename),
|
|
||||||
width = width, height = height }
|
|
||||||
|
|
||||||
images[index] = entry
|
|
||||||
job.percent = index / #images_ordered
|
|
||||||
index = index + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
stop_job(job)
|
|
||||||
gallery_data.images = images
|
|
||||||
return gallery_data
|
|
||||||
end
|
|
||||||
|
|
||||||
local function generate_javascript_gallery_object(gallery)
|
|
||||||
local js = 'const gallery_data = {\n'
|
|
||||||
js = js .. ' name: "' .. gallery.name .. '",\n'
|
|
||||||
js = js .. ' images: [\n'
|
|
||||||
|
|
||||||
for i, img in ipairs(gallery.images) do
|
|
||||||
js = js .. string.format(' { filename: "%s",\n height: %d,\n width: %d }', img.filename, img.height, img.width)
|
|
||||||
if i < #gallery.images then
|
|
||||||
js = js .. ',\n'
|
|
||||||
else
|
|
||||||
js = js .. '\n'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
js = js .. ' ]\n};\n'
|
|
||||||
|
|
||||||
return(js)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function write_javascript_file(gallery_table, dest_dir)
|
|
||||||
dt.print(_("write JavaScript file"))
|
|
||||||
javascript_object = generate_javascript_gallery_object(gallery_table)
|
|
||||||
|
|
||||||
local fileOut, errr = io.open(dest_dir .. PS .. "js" .. PS .. "images.js", 'w+')
|
|
||||||
if fileOut then
|
|
||||||
fileOut:write(javascript_object)
|
|
||||||
else
|
|
||||||
log.msg(log.error, errr)
|
|
||||||
end
|
|
||||||
fileOut:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function copy_static_files(dest_dir)
|
|
||||||
|
|
||||||
gfsrc = dt.configuration.config_dir .. PS .. "lua" .. PS .. "data" .. PS .. "website_gallery"
|
|
||||||
local gfiles = {
|
|
||||||
"index.html",
|
|
||||||
"css" .. PS .. "gallery.css",
|
|
||||||
"css" .. PS .. "modal.css",
|
|
||||||
"css" .. PS .. "style.css",
|
|
||||||
"js" .. PS .. "gallery.js",
|
|
||||||
"js" .. PS .. "modal.js",
|
|
||||||
"js" .. PS .. "fullscreen.js"
|
|
||||||
}
|
|
||||||
|
|
||||||
dt.print(_("copy static gallery files"))
|
|
||||||
for _, file in ipairs(gfiles) do
|
|
||||||
df.file_copy(gfsrc .. PS .. file, dest_dir .. PS .. file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function build_gallery(storage, images_table, extra_data)
|
|
||||||
local dest_dir = dest_dir_widget.value
|
|
||||||
df.mkdir(df.sanitize_filename(dest_dir))
|
|
||||||
df.mkdir(df.sanitize_filename(dest_dir .. PS .. "images"))
|
|
||||||
df.mkdir(df.sanitize_filename(dest_dir .. PS .. "thumbnails"))
|
|
||||||
df.mkdir(df.sanitize_filename(dest_dir .. PS .. "css"))
|
|
||||||
df.mkdir(df.sanitize_filename(dest_dir .. PS .. "js"))
|
|
||||||
|
|
||||||
local images_ordered = extra_data["images"] -- process images in the correct order
|
|
||||||
local sizes = extra_data["sizes"]
|
|
||||||
local title = _("Darktable export")
|
|
||||||
if title_widget.text ~= "" then
|
|
||||||
title = title_widget.text
|
|
||||||
end
|
|
||||||
local exiftool = df.check_if_bin_exists("exiftool");
|
|
||||||
gallerydata = fill_gallery_table(images_ordered, images_table, title, dest_dir, sizes, exiftool)
|
|
||||||
write_javascript_file(gallerydata, dest_dir)
|
|
||||||
copy_static_files(dest_dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("website gallery (new)"),
|
|
||||||
purpose = _("create a web gallery from exported images"),
|
|
||||||
author = "Tino Mettler <tino+darktable@tikei.de>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/TODO"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.preferences.write('web_gallery', 'title', 'string', title_widget.text)
|
|
||||||
dt.destroy_storage("module_webgallery")
|
|
||||||
end
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
local function show_status(storage, image, format, filename,
|
|
||||||
number, total, high_quality, extra_data)
|
|
||||||
dt.print(string.format(_("export image ").."%i/%i", number, total))
|
|
||||||
aspect = image.aspect_ratio
|
|
||||||
-- calculate the size of the exported image and store it in extra_data
|
|
||||||
-- to make it available in the finalize function
|
|
||||||
if image.final_height == 0 then
|
|
||||||
if aspect < 1 then
|
|
||||||
dimensions = { width = image.height, height = image.width }
|
|
||||||
else
|
|
||||||
dimensions = { width = image.width, height = image.height }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
dimensions = { width = image.final_width, height = image.final_height }
|
|
||||||
end
|
|
||||||
if format.max_height > 0 and dimensions.height > format.max_height then
|
|
||||||
scale = format.max_height / dimensions.height
|
|
||||||
dimensions.height = math.floor(dimensions.height * scale + 0.5)
|
|
||||||
dimensions.width = math.floor(dimensions.width * scale + 0.5)
|
|
||||||
end
|
|
||||||
if format.max_width > 0 and dimensions.width > format.max_width then
|
|
||||||
scale = format.max_width / dimensions.width
|
|
||||||
dimensions.height = math.floor(dimensions.height * scale + 0.5)
|
|
||||||
dimensions.width = math.floor(dimensions.width * scale + 0.5)
|
|
||||||
end
|
|
||||||
extra_data["sizes"][number] = dimensions
|
|
||||||
end
|
|
||||||
|
|
||||||
local function initialize(storage, img_format, images, high_quality, extra_data)
|
|
||||||
dt.preferences.write('web_gallery', 'title', 'string', title_widget.text)
|
|
||||||
extra_data["images"] = images -- needed, to preserve images order
|
|
||||||
extra_data["sizes"] = {};
|
|
||||||
end
|
|
||||||
|
|
||||||
local supported_formats = { "jpg", "tif", "png", "webp" }
|
|
||||||
|
|
||||||
local formats_lut = {}
|
|
||||||
for key,format in pairs(supported_formats) do
|
|
||||||
formats_lut[format] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
function check_supported(storage, format)
|
|
||||||
extension = format.extension
|
|
||||||
if formats_lut[extension] == true then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
dt.register_storage("module_webgallery", "website gallery (new)", show_status, build_gallery, check_supported, initialize, gallery_widget)
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.1 KiB |
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
copyright (c) 2025 Tino Mettler
|
|
||||||
|
|
||||||
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 this software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading h1 {
|
|
||||||
text-align: center;
|
|
||||||
display: grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery img {
|
|
||||||
cursor: pointer;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb {
|
|
||||||
object-fit: contain;
|
|
||||||
flex-shrink: 0;
|
|
||||||
min-width: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
object-fit: contain;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
copyright (c) 2025 Tino Mettler
|
|
||||||
|
|
||||||
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 this software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewer {
|
|
||||||
position: relative;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
touch-action: none;
|
|
||||||
overflow: hidden;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navgrid {
|
|
||||||
display: grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav {
|
|
||||||
height: var(--navigation-bar-height);
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
z-index: 1001;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider {
|
|
||||||
height: calc(100vh - var(--navigation-bar-height));
|
|
||||||
position: relative;
|
|
||||||
width: 100vw;
|
|
||||||
touch-action: none;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 300%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
transform: translateX(-33.333%);
|
|
||||||
transition: transform 0.3s ease-out;
|
|
||||||
will-change: transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide {
|
|
||||||
width: 33.333%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background: #000;
|
|
||||||
will-change: contents;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
touch-action: none;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
display: block;
|
|
||||||
transform-origin: 0 0;
|
|
||||||
cursor: zoom-in;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide.zoomed img {
|
|
||||||
cursor: zoom-out;
|
|
||||||
max-width: none;
|
|
||||||
max-height: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide img.zooming {
|
|
||||||
transition: transform 0.2s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
body {
|
|
||||||
font-family: system-ui, Arial, Helvetica, sans-serif;
|
|
||||||
background-color: rgb(238, 240, 242);
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading h1 {
|
|
||||||
background-color: #cdd;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading h1:after {
|
|
||||||
content: " ";
|
|
||||||
border-bottom: 1px solid #888888;
|
|
||||||
height: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery {
|
|
||||||
gap: 3vw;
|
|
||||||
margin: 3vw auto;
|
|
||||||
padding: 1.5vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery img {
|
|
||||||
background: none;
|
|
||||||
border: 1px outset #737780;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery img:hover {
|
|
||||||
transform: scale(1.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-box {
|
|
||||||
background: none;
|
|
||||||
box-shadow: 0 1vw 4vw 1vw rgba(0,0,0,0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewer {
|
|
||||||
--navigation-bar-height: 5vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav {
|
|
||||||
background-color: rgba(60,60,60, 0.4);
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navgrid {
|
|
||||||
grid-template-columns: 4em 1fr 1.5em 1.5em 1fr 1.5em 1.5em;
|
|
||||||
font-size: 3.5vh;
|
|
||||||
grid-column-gap: 1vw;
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
color: white;
|
|
||||||
text-shadow: 0px 0px 10px #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.counter {
|
|
||||||
font-size: 2.5vh;
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
||||||
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0">
|
|
||||||
<link rel="stylesheet" href="css/modal.css">
|
|
||||||
<link rel="stylesheet" href="css/gallery.css">
|
|
||||||
<link rel="stylesheet" href="css/style.css">
|
|
||||||
<title id="page-title">Image Gallery</title>
|
|
||||||
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="heading1" class="heading">
|
|
||||||
<h1 id="gallery-title">Image Gallery</h1>
|
|
||||||
</div>
|
|
||||||
<div id="gallery" class="gallery"></div>
|
|
||||||
<div class="viewer" id="viewer">
|
|
||||||
<div class="navgrid">
|
|
||||||
<div id="counter" class="nav"></div>
|
|
||||||
<div class="spacer"></div>
|
|
||||||
<div class="button nav nav-arrow" id="prevArrow">◀</div>
|
|
||||||
<div class="button nav nav-arrow" id="nextArrow">▶</div>
|
|
||||||
<div class="spacer"></div>
|
|
||||||
<div id="fullscreen" class="button nav">⛶</div>
|
|
||||||
<div id="close" class="nav button">×</div>
|
|
||||||
</div>
|
|
||||||
<div class="slider" id="slider1">
|
|
||||||
<div class="slide-container">
|
|
||||||
<div class="slide" id="prev"></div>
|
|
||||||
<div class="slide" id="current"></div>
|
|
||||||
<div class="slide" id="next"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type='text/javascript' >
|
|
||||||
var currentIndex = 1;
|
|
||||||
</script>
|
|
||||||
<script src="js/images.js"></script>
|
|
||||||
<script src="js/fullscreen.js"></script>
|
|
||||||
<script src="js/modal.js"></script>
|
|
||||||
<script src="js/gallery.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
var isFullscreen = false;
|
|
||||||
|
|
||||||
var toggleFullscreen = function (ele) {
|
|
||||||
return isFullscreen ? exitFullscreen(ele) : requestFullscreen(ele);
|
|
||||||
};
|
|
||||||
|
|
||||||
var requestFullscreen = function (ele) {
|
|
||||||
if(isFullscreen == true) return 0 ;
|
|
||||||
|
|
||||||
isFullscreen = true;
|
|
||||||
if (ele.requestFullscreen) {
|
|
||||||
ele.requestFullscreen();
|
|
||||||
} else if (ele.webkitRequestFullscreen) {
|
|
||||||
ele.webkitRequestFullscreen();
|
|
||||||
} else if (ele.mozRequestFullScreen) {
|
|
||||||
ele.mozRequestFullScreen();
|
|
||||||
} else if (ele.msRequestFullscreen) {
|
|
||||||
ele.msRequestFullscreen();
|
|
||||||
} else {
|
|
||||||
console.log('Fullscreen API is not supported.');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var exitFullscreen = function () {
|
|
||||||
if(isFullscreen == false) return 0;
|
|
||||||
|
|
||||||
isFullscreen = false;
|
|
||||||
if (document.exitFullscreen) {
|
|
||||||
document.exitFullscreen();
|
|
||||||
} else if (document.webkitExitFullscreen) {
|
|
||||||
document.webkitExitFullscreen();
|
|
||||||
} else if (document.mozCancelFullScreen) {
|
|
||||||
document.mozCancelFullScreen();
|
|
||||||
} else if (document.msExitFullscreen) {
|
|
||||||
document.msExitFullscreen();
|
|
||||||
} else {
|
|
||||||
console.log('Fullscreen API is not supported.');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
copyright (c) 2025 Tino Mettler
|
|
||||||
|
|
||||||
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 this software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var scrollPosX = 0;
|
|
||||||
var scrollPosY = 0;
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
var imageCount = 0;
|
|
||||||
const gallery = document.getElementById('gallery');
|
|
||||||
const viewer = document.getElementById('viewer');
|
|
||||||
|
|
||||||
function showModal(e) {
|
|
||||||
const thumbbox = e.target.parentElement;
|
|
||||||
const index = [...gallery.children].indexOf(thumbbox);
|
|
||||||
currentIndex = index;
|
|
||||||
scrollPosX = document.documentElement.scrollLeft;
|
|
||||||
scrollPosY = document.documentElement.scrollTop;
|
|
||||||
|
|
||||||
gallery.style.display = 'none';
|
|
||||||
document.getElementById('heading1').style.display = 'none';
|
|
||||||
viewer.style.display = 'grid';
|
|
||||||
loadSlides();
|
|
||||||
updateCounter(currentIndex);
|
|
||||||
updateNavigationState();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function closeModal() {
|
|
||||||
exitFullscreen(document.documentElement);
|
|
||||||
viewer.style.display = 'none';
|
|
||||||
document.getElementById('heading1').style.display = 'grid';
|
|
||||||
gallery.style.display = 'flex';
|
|
||||||
document.documentElement.scrollTo({
|
|
||||||
left: scrollPosX,
|
|
||||||
top: scrollPosY,
|
|
||||||
behavior: "instant",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function createThumbnailElement(imageObj) {
|
|
||||||
const frame = document.createElement('div');
|
|
||||||
frame.className = 'thumb-box';
|
|
||||||
const framesize = 18;
|
|
||||||
|
|
||||||
const width = parseInt(imageObj.width);
|
|
||||||
const height = parseInt(imageObj.height);
|
|
||||||
const sum = width + height;
|
|
||||||
const scalefactor = sum / (framesize * 2.0);
|
|
||||||
frame.style.width = (width / scalefactor) + 'vw';
|
|
||||||
frame.style.height = (height / scalefactor) + 'vw';
|
|
||||||
|
|
||||||
const img = document.createElement('img');
|
|
||||||
img.className = 'thumb';
|
|
||||||
img.src = imageObj.filename.replace(/images\/(.*)$/i, 'thumbnails/thumb_$1');
|
|
||||||
img.alt = imageObj.filename;
|
|
||||||
img.addEventListener('click', function (e) { e.stopPropagation(); showModal(e); });
|
|
||||||
|
|
||||||
frame.appendChild(img);
|
|
||||||
gallery.appendChild(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
const images = gallery_data.images;
|
|
||||||
|
|
||||||
const title = document.getElementById('gallery-title');
|
|
||||||
const pageTitle = document.getElementById('page-title');
|
|
||||||
if (gallery_data.name) {
|
|
||||||
title.textContent = gallery_data.name;
|
|
||||||
pageTitle.textContent = gallery_data.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
document.getElementById('close').onclick = function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
closeModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Keyboard navigation using left/right arrow keys
|
|
||||||
document.onkeyup = function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
switch(e.key) {
|
|
||||||
case "Escape":
|
|
||||||
closeModal();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById('fullscreen').onclick = function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
toggleFullscreen(document.documentElement);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Populate thumbnail gallery
|
|
||||||
images.forEach(function (imageObj) {
|
|
||||||
createThumbnailElement(imageObj);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
/*
|
|
||||||
copyright (c) 2025 Tino Mettler
|
|
||||||
|
|
||||||
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 this software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const container = document.querySelector('.slide-container');
|
|
||||||
const slides = {
|
|
||||||
prev: document.getElementById('prev'),
|
|
||||||
current: document.getElementById('current'),
|
|
||||||
next: document.getElementById('next')
|
|
||||||
};
|
|
||||||
const prevArrow = document.getElementById('prevArrow');
|
|
||||||
const nextArrow = document.getElementById('nextArrow');
|
|
||||||
|
|
||||||
let startX = 0;
|
|
||||||
let startY = 0;
|
|
||||||
let isDragging = false; // For swipe navigation
|
|
||||||
let isZoomed = false;
|
|
||||||
let isPanning = false; // Panning in zoomed state
|
|
||||||
let hasPanned = false;
|
|
||||||
let translateX = 0;
|
|
||||||
let translateY = 0;
|
|
||||||
let currentScale = 1;
|
|
||||||
let img;
|
|
||||||
|
|
||||||
let baseWidth, baseHeight; // Image dimensions before zoom
|
|
||||||
|
|
||||||
let scaledWidth, scaledHeight;
|
|
||||||
|
|
||||||
const images = gallery_data.images;
|
|
||||||
|
|
||||||
function updateCounter(index) {
|
|
||||||
const counter = document.getElementById('counter');
|
|
||||||
counter.textContent = (index + 1) + ' / ' + images.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateBoundaries() {
|
|
||||||
containerRect = container.getBoundingClientRect();
|
|
||||||
// Calculate the actual scaled dimensions based on the base (pre-zoom) size
|
|
||||||
scaledWidth = baseWidth * currentScale;
|
|
||||||
scaledHeight = baseHeight * currentScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
function limitPanning(proposedX, proposedY) {
|
|
||||||
const maxX = 0;
|
|
||||||
const minX = baseWidth - scaledWidth;
|
|
||||||
|
|
||||||
const maxY = 0;
|
|
||||||
const minY = baseHeight - scaledHeight;
|
|
||||||
|
|
||||||
let constrainedX = proposedX;
|
|
||||||
let constrainedY = proposedY;
|
|
||||||
|
|
||||||
// Apply constraints
|
|
||||||
if (scaledWidth > baseWidth) {
|
|
||||||
// Image wider than original - constrain panning
|
|
||||||
constrainedX = Math.max(minX, Math.min(maxX, proposedX));
|
|
||||||
} else {
|
|
||||||
// Image narrower than original - center it
|
|
||||||
constrainedX = (baseWidth - scaledWidth) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scaledHeight > baseHeight) {
|
|
||||||
// Image taller than original - constrain panning
|
|
||||||
constrainedY = Math.max(minY, Math.min(maxY, proposedY));
|
|
||||||
} else {
|
|
||||||
// Image shorter than original - center it
|
|
||||||
constrainedY = (baseHeight - scaledHeight) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
x: constrainedX,
|
|
||||||
y: constrainedY
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleZoom(e) {
|
|
||||||
// If we were actually panning, don't zoom
|
|
||||||
if (hasPanned) {
|
|
||||||
isPanning = false;
|
|
||||||
hasPanned = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're zoomed and haven't panned, zoom out
|
|
||||||
if (isZoomed) {
|
|
||||||
img.classList.add('zooming');
|
|
||||||
translateX = 0;
|
|
||||||
translateY = 0;
|
|
||||||
currentScale = 1;
|
|
||||||
img.style.transform = 'none';
|
|
||||||
container.classList.remove('zoomed');
|
|
||||||
isZoomed = false;
|
|
||||||
updateNavigationState();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
img.classList.remove('zooming');
|
|
||||||
}, 200);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zoom in at clicked/tapped point
|
|
||||||
if (!isZoomed) {
|
|
||||||
const rect = img.getBoundingClientRect();
|
|
||||||
const x = e.clientX - rect.left;
|
|
||||||
const y = e.clientY - rect.top;
|
|
||||||
|
|
||||||
// Store the base (pre-zoom) dimensions and offset
|
|
||||||
baseWidth = rect.width;
|
|
||||||
baseHeight = rect.height;
|
|
||||||
|
|
||||||
// Calculate the scale for 1:1 pixel zoom
|
|
||||||
currentScale = img.naturalWidth / rect.width;
|
|
||||||
|
|
||||||
// Calculate the translation needed to keep clicked point under cursor
|
|
||||||
// After scaling, the point at (x, y) will be at (x * currentScale, y * currentScale)
|
|
||||||
// We want it to remain at (e.clientX - rect.left, e.clientY - rect.top)
|
|
||||||
translateX = e.clientX - rect.left - (x * currentScale);
|
|
||||||
translateY = e.clientY - rect.top - (y * currentScale);
|
|
||||||
|
|
||||||
updateBoundaries();
|
|
||||||
const limited = limitPanning(translateX, translateY);
|
|
||||||
translateX = limited.x;
|
|
||||||
translateY = limited.y;
|
|
||||||
|
|
||||||
img.classList.add('zooming');
|
|
||||||
img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${currentScale})`;
|
|
||||||
container.classList.add('zoomed');
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
img.classList.remove('zooming');
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
isZoomed = true;
|
|
||||||
updateNavigationState();
|
|
||||||
}
|
|
||||||
|
|
||||||
function createImageElement(imageData) {
|
|
||||||
if (!imageData) return null;
|
|
||||||
const img = new Image();
|
|
||||||
img.src = imageData.filename;
|
|
||||||
img.width = imageData.width;
|
|
||||||
img.height = imageData.height;
|
|
||||||
img.addEventListener('dragstart', (e) => e.preventDefault());
|
|
||||||
return img;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadSlides() {
|
|
||||||
slides.prev.innerHTML = '';
|
|
||||||
slides.current.innerHTML = '';
|
|
||||||
slides.next.innerHTML = '';
|
|
||||||
|
|
||||||
if (currentIndex > 0) {
|
|
||||||
const prevImg = createImageElement(images[currentIndex - 1]);
|
|
||||||
if (prevImg) slides.prev.appendChild(prevImg);
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentImg = createImageElement(images[currentIndex]);
|
|
||||||
if (currentImg) {
|
|
||||||
img = currentImg;
|
|
||||||
slides.current.appendChild(currentImg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentIndex < images.length - 1) {
|
|
||||||
const nextImg = createImageElement(images[currentIndex + 1]);
|
|
||||||
if (nextImg) slides.next.appendChild(nextImg);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCounter(currentIndex)
|
|
||||||
updateNavigationState();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNavigationState() {
|
|
||||||
prevArrow.style.visibility = (currentIndex === 0 || isZoomed) ? 'hidden' : 'visible';
|
|
||||||
nextArrow.style.visibility = (currentIndex === images.length - 1 || isZoomed) ? 'hidden' : 'visible';
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showPreviousImage() {
|
|
||||||
if (currentIndex > 0 && !isZoomed) {
|
|
||||||
currentIndex--;
|
|
||||||
container.style.transition = 'transform 0.3s ease-out';
|
|
||||||
container.style.transform = 'translateX(0%)';
|
|
||||||
await waitForTransition();
|
|
||||||
container.style.transition = 'none';
|
|
||||||
container.style.transform = 'translateX(-33.333%)';
|
|
||||||
loadSlides();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showNextImage() {
|
|
||||||
if (currentIndex < images.length - 1 && !isZoomed) {
|
|
||||||
currentIndex++;
|
|
||||||
container.style.transition = 'transform 0.3s ease-out';
|
|
||||||
container.style.transform = 'translateX(-66.666%)';
|
|
||||||
await waitForTransition();
|
|
||||||
container.style.transition = 'none';
|
|
||||||
container.style.transform = 'translateX(-33.333%)';
|
|
||||||
loadSlides();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTouchStart(e) {
|
|
||||||
if (isZoomed) return;
|
|
||||||
startX = e.touches[0].clientX;
|
|
||||||
isDragging = true;
|
|
||||||
container.style.transition = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTouchMove(e) {
|
|
||||||
if (!isDragging || isZoomed) return;
|
|
||||||
|
|
||||||
const currentX = e.touches[0].clientX;
|
|
||||||
const diff = currentX - startX;
|
|
||||||
const baseOffset = -33.333;
|
|
||||||
const percentMoved = (diff / window.innerWidth) * 33.333;
|
|
||||||
|
|
||||||
container.style.transform = `translateX(${baseOffset + percentMoved}%)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleTouchEnd(e) {
|
|
||||||
if (!isDragging)
|
|
||||||
if(isZoomed) {
|
|
||||||
handleZoom(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isDragging = false;
|
|
||||||
|
|
||||||
const endX = e.changedTouches[0].clientX;
|
|
||||||
const diff = endX - startX;
|
|
||||||
const threshold = window.innerWidth * 0.2;
|
|
||||||
|
|
||||||
container.style.transition = 'transform 0.3s ease-out';
|
|
||||||
|
|
||||||
if (diff > threshold && currentIndex > 0) {
|
|
||||||
await showPreviousImage();
|
|
||||||
} else if (diff < -threshold && currentIndex < images.length - 1) {
|
|
||||||
await showNextImage();
|
|
||||||
} else {
|
|
||||||
container.style.transform = 'translateX(-33.333%)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleKeyDown(e) {
|
|
||||||
if (isZoomed) return;
|
|
||||||
|
|
||||||
if (e.code === 'Space' || e.code === 'ArrowRight') {
|
|
||||||
e.preventDefault();
|
|
||||||
showNextImage();
|
|
||||||
} else if (e.code === 'Backspace' || e.code === 'ArrowLeft') {
|
|
||||||
e.preventDefault();
|
|
||||||
showPreviousImage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function waitForTransition() {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
container.addEventListener('transitionend', resolve, { once: true });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
loadSlides();
|
|
||||||
|
|
||||||
// Navigation event listeners
|
|
||||||
container.addEventListener('touchstart', handleTouchStart);
|
|
||||||
container.addEventListener('touchmove', handleTouchMove);
|
|
||||||
container.addEventListener('touchend', handleTouchEnd);
|
|
||||||
container.addEventListener('click', handleZoom);
|
|
||||||
|
|
||||||
document.addEventListener('keydown', handleKeyDown);
|
|
||||||
prevArrow.addEventListener('click', showPreviousImage);
|
|
||||||
nextArrow.addEventListener('click', showNextImage);
|
|
||||||
|
|
||||||
// Mouse panning event listeners
|
|
||||||
container.addEventListener('mousedown', function(e) {
|
|
||||||
if (isZoomed && e.target.tagName === 'IMG') {
|
|
||||||
isPanning = true;
|
|
||||||
hasPanned = false;
|
|
||||||
updateBoundaries();
|
|
||||||
startX = e.clientX - translateX;
|
|
||||||
startY = e.clientY - translateY;
|
|
||||||
e.preventDefault();
|
|
||||||
container.style.cursor = 'grabbing';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('mousemove', function(e) {
|
|
||||||
if (isPanning && isZoomed) {
|
|
||||||
const proposedX = e.clientX - startX;
|
|
||||||
const proposedY = e.clientY - startY;
|
|
||||||
|
|
||||||
const limited = limitPanning(proposedX, proposedY);
|
|
||||||
translateX = limited.x;
|
|
||||||
translateY = limited.y;
|
|
||||||
|
|
||||||
const img = slides.current.querySelector('img');
|
|
||||||
img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${currentScale})`;
|
|
||||||
hasPanned = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('mouseup', function() {
|
|
||||||
if (isPanning) {
|
|
||||||
isPanning = false;
|
|
||||||
container.style.cursor = isZoomed ? 'zoom-out' : 'zoom-in';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Touch panning event listeners
|
|
||||||
container.addEventListener('touchstart', function(e) {
|
|
||||||
if (isZoomed) {
|
|
||||||
isPanning = true;
|
|
||||||
hasPanned = false;
|
|
||||||
updateBoundaries();
|
|
||||||
const touch = e.touches[0];
|
|
||||||
startX = touch.clientX - translateX;
|
|
||||||
startY = touch.clientY - translateY;
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
container.addEventListener('touchmove', function(e) {
|
|
||||||
if (isPanning && isZoomed) {
|
|
||||||
const touch = e.touches[0];
|
|
||||||
const proposedX = touch.clientX - startX;
|
|
||||||
const proposedY = touch.clientY - startY;
|
|
||||||
|
|
||||||
const limited = limitPanning(proposedX, proposedY);
|
|
||||||
translateX = limited.x;
|
|
||||||
translateY = limited.y;
|
|
||||||
|
|
||||||
const img = slides.current.querySelector('img');
|
|
||||||
img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${currentScale})`;
|
|
||||||
hasPanned = true;
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
container.addEventListener('touchend', function(e) {
|
|
||||||
if (isPanning) {
|
|
||||||
isPanning = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -23,14 +23,6 @@ USAGE
|
|||||||
]]
|
]]
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
|
|
||||||
-- translation facilities
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- script_manager integration to allow a script to be removed
|
-- script_manager integration to allow a script to be removed
|
||||||
-- without restarting darktable
|
-- without restarting darktable
|
||||||
local function destroy()
|
local function destroy()
|
||||||
@@ -38,21 +30,12 @@ local function destroy()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local result = dt.configuration.api_version_string
|
local result = dt.configuration.api_version_string
|
||||||
dt.print_log("API Version: " .. result)
|
dt.print_error("API Version: "..result)
|
||||||
dt.print("API " .. _("version") .. ": " .. result)
|
|
||||||
|
|
||||||
-- set the destroy routine so that script_manager can call it when
|
-- set the destroy routine so that script_manager can call it when
|
||||||
-- it's time to destroy the script and then return the data to
|
-- it's time to destroy the script and then return the data to
|
||||||
-- script_manager
|
-- script_manager
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("APIversion"),
|
|
||||||
purpose = _("display api_version example"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/api_version"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ local du = require "lib/dtutils"
|
|||||||
-- V E R S I O N C H E C K
|
-- V E R S I O N C H E C K
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
du.check_min_api_version("5.0.2", "darkroom_demo") -- darktable 3.0
|
du.check_min_api_version("5.0.2", "darkroom_mode") -- darktable 3.0
|
||||||
|
|
||||||
-- script_manager integration to allow a script to be removed
|
-- script_manager integration to allow a script to be removed
|
||||||
-- without restarting darktable
|
-- without restarting darktable
|
||||||
@@ -52,16 +52,17 @@ end
|
|||||||
-- C O N S T A N T S
|
-- C O N S T A N T S
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
local MODULE_NAME = "darkroom_demo"
|
local MODULE_NAME = "darkroom"
|
||||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- T R A N S L A T I O N S
|
-- T R A N S L A T I O N S
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext(MODULE_NAME, msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@@ -91,13 +92,13 @@ dt.gui.current_view(dt.gui.views.darkroom)
|
|||||||
|
|
||||||
local max_images = 10
|
local max_images = 10
|
||||||
|
|
||||||
dt.print(_("showing images, with a pause between each"))
|
dt.print(_("Showing images, with a pause in between each"))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
|
|
||||||
-- display first 10 images of collection pausing for a second between each
|
-- display first 10 images of collection pausing for a second between each
|
||||||
|
|
||||||
for i, img in ipairs(dt.collection) do
|
for i, img in ipairs(dt.collection) do
|
||||||
dt.print(string.format(_("displaying image "), i))
|
dt.print(_("Displaying image " .. i))
|
||||||
dt.gui.views.darkroom.display_image(img)
|
dt.gui.views.darkroom.display_image(img)
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
if i == max_images then
|
if i == max_images then
|
||||||
@@ -107,7 +108,7 @@ end
|
|||||||
|
|
||||||
-- return to lighttable view
|
-- return to lighttable view
|
||||||
|
|
||||||
dt.print(_("restoring view"))
|
dt.print(_("Restoring view"))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
dt.gui.current_view(current_view)
|
dt.gui.current_view(current_view)
|
||||||
|
|
||||||
@@ -115,14 +116,6 @@ dt.gui.current_view(current_view)
|
|||||||
-- it's time to destroy the script and then return the data to
|
-- it's time to destroy the script and then return the data to
|
||||||
-- script_manager
|
-- script_manager
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("darkroom demo"),
|
|
||||||
purpose = _("example demonstrating how to control image display in darkroom mode"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/darkroom_demo"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -67,31 +67,27 @@ end
|
|||||||
-- Not translated Text
|
-- Not translated Text
|
||||||
dt.print_error("Hello World!")
|
dt.print_error("Hello World!")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
-- Translate a string using the darktable textdomain
|
-- Translate a string using the darktable textdomain
|
||||||
dt.print_error(gettext("image"))
|
dt.print_error(gettext.gettext("image"))
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
|
||||||
|
gettext.bindtextdomain("gettextExample",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
-- Translate a string using the specified textdomain
|
||||||
|
dt.print_error(gettext.dgettext("gettextExample", 'Hello World!'))
|
||||||
|
|
||||||
-- Define a local function called _ to make the code more readable and have it call dgettext
|
-- Define a local function called _ to make the code more readable and have it call dgettext
|
||||||
-- with the proper domain.
|
-- with the proper domain.
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext("gettextExample", msgid)
|
||||||
end
|
end
|
||||||
|
dt.print_error(_('Hello World!'))
|
||||||
dt.print_error(_("hello world!"))
|
|
||||||
|
|
||||||
-- set the destroy routine so that script_manager can call it when
|
-- set the destroy routine so that script_manager can call it when
|
||||||
-- it's time to destroy the script and then return the data to
|
-- it's time to destroy the script and then return the data to
|
||||||
-- script_manager
|
-- script_manager
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("gettext example"),
|
|
||||||
purpose = _("example of how translations works"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/gettextExample"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -4,61 +4,39 @@ local NaN = 0/0
|
|||||||
|
|
||||||
local wg = {}
|
local wg = {}
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("gui action"),
|
|
||||||
purpose = _("example of how to use darktable.gui.action() calls"),
|
|
||||||
author = "Diederik ter Rahe",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/gui_action"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
wg.action = dt.new_widget("entry"){
|
wg.action = dt.new_widget("entry"){
|
||||||
text = "lib/filter/view",
|
text = "lib/filter/view",
|
||||||
placeholder = _("action path"),
|
placeholder = "action path",
|
||||||
tooltip = _("enter the full path of an action, for example 'lib/filter/view'")
|
tooltip = "enter the full path of an action, for example 'lib/filter/view'"
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.instance = dt.new_widget("combobox"){
|
wg.instance = dt.new_widget("combobox"){
|
||||||
label = _("instance"),
|
label = "instance",
|
||||||
tooltip = _("the instance of an image processing module to execute action on"),
|
tooltip = "the instance of an image processing module to execute action on",
|
||||||
"0", "+1", "-1", "+2", "-2", "+3", "-3", "+4", "-4", "+5", "-5", "+6", "-6", "+7", "-7", "+8", "-8", "+9", "-9"
|
"0", "+1", "-1", "+2", "-2", "+3", "-3", "+4", "-4", "+5", "-5", "+6", "-6", "+7", "-7", "+8", "-8", "+9", "-9"
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.element = dt.new_widget("entry"){
|
wg.element = dt.new_widget("entry"){
|
||||||
text = "",
|
text = "",
|
||||||
placeholder = _("action element"),
|
placeholder = "action element",
|
||||||
tooltip = _("enter the element of an action, for example 'selection', or leave empty for default")
|
tooltip = "enter the element of an action, for example 'selection', or leave empty for default"
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.effect = dt.new_widget("entry"){
|
wg.effect = dt.new_widget("entry"){
|
||||||
text = "next",
|
text = "next",
|
||||||
placeholder = _("action effect"),
|
placeholder = "action effect",
|
||||||
tooltip = _("enter the effect of an action, for example 'next', or leave empty for default")
|
tooltip = "enter the effect of an action, for example 'next', or leave empty for default"
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.speed = dt.new_widget("entry"){
|
wg.speed = dt.new_widget("entry"){
|
||||||
text = "1",
|
text = "1",
|
||||||
placeholder = _("action speed"),
|
placeholder = "action speed",
|
||||||
tooltip = _("enter the speed to use in action execution, or leave empty to only read state")
|
tooltip = "enter the speed to use in action execution, or leave empty to only read state"
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.check = dt.new_widget("check_button"){
|
wg.check = dt.new_widget("check_button"){
|
||||||
label = _('perform action'),
|
label = 'perform action',
|
||||||
tooltip = _('perform action or only read return'),
|
tooltip = 'perform action or only read return',
|
||||||
clicked_callback = function()
|
clicked_callback = function()
|
||||||
wg.speed.sensitive = wg.check.value
|
wg.speed.sensitive = wg.check.value
|
||||||
end,
|
end,
|
||||||
@@ -72,7 +50,7 @@ wg.return_value = dt.new_widget("entry"){
|
|||||||
|
|
||||||
dt.register_lib(
|
dt.register_lib(
|
||||||
"execute_action", -- Module name
|
"execute_action", -- Module name
|
||||||
_("execute gui actions"), -- name
|
"execute gui actions", -- name
|
||||||
true, -- expandable
|
true, -- expandable
|
||||||
false, -- resetable
|
false, -- resetable
|
||||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 100},
|
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 100},
|
||||||
@@ -84,37 +62,37 @@ dt.register_lib(
|
|||||||
dt.new_widget("box")
|
dt.new_widget("box")
|
||||||
{
|
{
|
||||||
orientation = "horizontal",
|
orientation = "horizontal",
|
||||||
dt.new_widget("label"){label = _("action path"), halign = "start"},
|
dt.new_widget("label"){label = "action path", halign = "start"},
|
||||||
wg.action
|
wg.action
|
||||||
},
|
},
|
||||||
wg.instance,
|
wg.instance,
|
||||||
dt.new_widget("box")
|
dt.new_widget("box")
|
||||||
{
|
{
|
||||||
orientation = "horizontal",
|
orientation = "horizontal",
|
||||||
dt.new_widget("label"){label = _("element"), halign = "start"},
|
dt.new_widget("label"){label = "element", halign = "start"},
|
||||||
wg.element
|
wg.element
|
||||||
},
|
},
|
||||||
dt.new_widget("box")
|
dt.new_widget("box")
|
||||||
{
|
{
|
||||||
orientation = "horizontal",
|
orientation = "horizontal",
|
||||||
dt.new_widget("label"){label = _("effect"), halign = "start"},
|
dt.new_widget("label"){label = "effect", halign = "start"},
|
||||||
wg.effect
|
wg.effect
|
||||||
},
|
},
|
||||||
wg.check,
|
wg.check,
|
||||||
dt.new_widget("box")
|
dt.new_widget("box")
|
||||||
{
|
{
|
||||||
orientation = "horizontal",
|
orientation = "horizontal",
|
||||||
dt.new_widget("label"){label = _("speed"), halign = "start"},
|
dt.new_widget("label"){label = "speed", halign = "start"},
|
||||||
wg.speed
|
wg.speed
|
||||||
},
|
},
|
||||||
dt.new_widget("button")
|
dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = _("execute action"),
|
label = "execute action",
|
||||||
tooltip = _("execute the action specified in the fields above"),
|
tooltip = "execute the action specified in the fields above",
|
||||||
clicked_callback = function(_)
|
clicked_callback = function(_)
|
||||||
local sp = NaN
|
local sp = NaN
|
||||||
if wg.check.value then sp = wg.speed.text end
|
if wg.check.value then sp = wg.speed.text end
|
||||||
wg.return_value.text = dt.gui.action(wg.action.text, tonumber(wg.instance.value), wg.element.text, wg.effect.text, tonumber(sp))
|
wg.return_value.text = dt.gui.action(wg.action.text, wg.instance.value, wg.element.text, wg.effect.text, sp)
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
dt.new_widget("box")
|
dt.new_widget("box")
|
||||||
@@ -125,18 +103,3 @@ dt.register_lib(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
local function restart()
|
|
||||||
dt.gui.libs["execute_action"].visible = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.gui.libs["execute_action"].visible = false
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
script_data.destroy_method = "hide"
|
|
||||||
script_data.restart = restart
|
|
||||||
script_data.show = restart
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
|
|||||||
@@ -29,36 +29,20 @@ USAGE
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
|
|
||||||
-- translation facilities
|
|
||||||
|
|
||||||
du.check_min_api_version("2.0.0", "hello_world")
|
du.check_min_api_version("2.0.0", "hello_world")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- script_manager integration to allow a script to be removed
|
-- script_manager integration to allow a script to be removed
|
||||||
-- without restarting darktable
|
-- without restarting darktable
|
||||||
local function destroy()
|
local function destroy()
|
||||||
-- nothing to destroy
|
-- nothing to destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
dt.print(_("hello, world"))
|
dt.print("hello, world")
|
||||||
|
|
||||||
-- set the destroy routine so that script_manager can call it when
|
-- set the destroy routine so that script_manager can call it when
|
||||||
-- it's time to destroy the script and then return the data to
|
-- it's time to destroy the script and then return the data to
|
||||||
-- script_manager
|
-- script_manager
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("hello world"),
|
|
||||||
purpose = _("example of how to print a message to the screen"),
|
|
||||||
author = "Tobias Ellinghaus",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/hello_world"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -59,12 +59,11 @@ end
|
|||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- T R A N S L A T I O N S
|
-- T R A N S L A T I O N S
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
|
||||||
dt.gettext.bindtextdomain("lighttable_demo", dt.configuration.config_dir .."/lua/locale/")
|
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext(MODULE_NAME, msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@@ -146,17 +145,17 @@ sleep(2000)
|
|||||||
|
|
||||||
for n, layout in ipairs(layouts) do
|
for n, layout in ipairs(layouts) do
|
||||||
dt.gui.libs.lighttable_mode.layout(layout)
|
dt.gui.libs.lighttable_mode.layout(layout)
|
||||||
dt.print(string.format(_("set lighttable layout to %s"), layout))
|
dt.print(_("set lighttable layout to " .. layout))
|
||||||
dt.print_log("set lighttable layout to " .. layout)
|
dt.print_log(_("set lighttable layout to " .. layout))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
for i = 1, 10 do
|
for i = 1, 10 do
|
||||||
dt.gui.libs.lighttable_mode.zoom_level(i)
|
dt.gui.libs.lighttable_mode.zoom_level(i)
|
||||||
dt.print(string.format(_("set zoom level to %d"), i))
|
dt.print(_("Set zoom level to " .. i))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
end
|
end
|
||||||
for i = 9, 1, -1 do
|
for i = 9, 1, -1 do
|
||||||
dt.gui.libs.lighttable_mode.zoom_level(i)
|
dt.gui.libs.lighttable_mode.zoom_level(i)
|
||||||
dt.print(string.format(_("set zoom level to %d"), i))
|
dt.print(_("Set zoom level to " .. i))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -175,12 +174,12 @@ dt.print_log("starting sorts")
|
|||||||
|
|
||||||
for n, sort in ipairs(sorts) do
|
for n, sort in ipairs(sorts) do
|
||||||
dt.gui.libs.filter.sort(sort)
|
dt.gui.libs.filter.sort(sort)
|
||||||
dt.print(string.format(_("set lighttable sort to %s"), sort))
|
dt.print(_("set lighttable sort to " .. sort))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
|
|
||||||
for m, sort_order in ipairs(sort_orders) do
|
for m, sort_order in ipairs(sort_orders) do
|
||||||
dt.gui.libs.filter.sort_order(sort_order)
|
dt.gui.libs.filter.sort_order(sort_order)
|
||||||
dt.print(string.format(_("sort order set to %s"), sort_order))
|
dt.print(_("sort order set to " .. sort_order))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -191,12 +190,12 @@ dt.print(_("lighttable filtering demonstration"))
|
|||||||
|
|
||||||
for n, rating in ipairs(ratings) do
|
for n, rating in ipairs(ratings) do
|
||||||
dt.gui.libs.filter.rating(rating)
|
dt.gui.libs.filter.rating(rating)
|
||||||
dt.print(string.format(_("set filter to %s"), rating))
|
dt.print(_("set filter to " .. rating))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
|
|
||||||
for m, rating_comparator in ipairs(rating_comparators) do
|
for m, rating_comparator in ipairs(rating_comparators) do
|
||||||
dt.gui.libs.filter.rating_comparator(rating_comparator)
|
dt.gui.libs.filter.rating_comparator(rating_comparator)
|
||||||
dt.print(string.format(_("set rating comparator to %s"), rating_comparator))
|
dt.print(_("set rating comparator to " .. rating_comparator))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -216,14 +215,6 @@ current_sort_order = dt.gui.libs.filter.sort_order(current_sort_order)
|
|||||||
-- it's time to destroy the script and then return the data to
|
-- it's time to destroy the script and then return the data to
|
||||||
-- script_manager
|
-- script_manager
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("lighttable demo"),
|
|
||||||
purpose = _("example demonstrating how to control lighttable display modes"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/lighttable_demo"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -35,24 +35,10 @@ local du = require "lib/dtutils"
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", "moduleExample")
|
du.check_min_api_version("7.0.0", "moduleExample")
|
||||||
|
|
||||||
-- https://www.darktable.org/lua-api/index.html#darktable_gettext
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = ("module example"),
|
|
||||||
purpose = _("example of how to create a lighttable module"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/moduleExample"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -60,6 +46,15 @@ script_data.show = nil -- only required for libs since the destroy_method only h
|
|||||||
|
|
||||||
-- translation
|
-- translation
|
||||||
|
|
||||||
|
-- https://www.darktable.org/lua-api/index.html#darktable_gettext
|
||||||
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
gettext.bindtextdomain("moduleExample", dt.configuration.config_dir .. "/lua/locale/")
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("moduleExample", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
-- declare a local namespace and a couple of variables we'll need to install the module
|
-- declare a local namespace and a couple of variables we'll need to install the module
|
||||||
local mE = {}
|
local mE = {}
|
||||||
mE.widgets = {}
|
mE.widgets = {}
|
||||||
@@ -75,7 +70,7 @@ local function install_module()
|
|||||||
-- https://www.darktable.org/lua-api/index.html#darktable_register_lib
|
-- https://www.darktable.org/lua-api/index.html#darktable_register_lib
|
||||||
dt.register_lib(
|
dt.register_lib(
|
||||||
"exampleModule", -- Module name
|
"exampleModule", -- Module name
|
||||||
_("example module"), -- name
|
"exampleModule", -- name
|
||||||
true, -- expandable
|
true, -- expandable
|
||||||
false, -- resetable
|
false, -- resetable
|
||||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||||
@@ -85,9 +80,9 @@ local function install_module()
|
|||||||
orientation = "vertical",
|
orientation = "vertical",
|
||||||
dt.new_widget("button")
|
dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = _("my ") .. "button",
|
label = _("MyButton"),
|
||||||
clicked_callback = function (_)
|
clicked_callback = function (_)
|
||||||
dt.print(_("button clicked"))
|
dt.print(_("Button clicked"))
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
table.unpack(mE.widgets),
|
table.unpack(mE.widgets),
|
||||||
@@ -110,10 +105,10 @@ local function restart()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- https://www.darktable.org/lua-api/types_lua_check_button.html
|
-- https://www.darktable.org/lua-api/types_lua_check_button.html
|
||||||
local check_button = dt.new_widget("check_button"){label = _("my ") .. "check_button", value = true}
|
local check_button = dt.new_widget("check_button"){label = _("MyCheck_button"), value = true}
|
||||||
|
|
||||||
-- https://www.darktable.org/lua-api/types_lua_combobox.html
|
-- https://www.darktable.org/lua-api/types_lua_combobox.html
|
||||||
local combobox = dt.new_widget("combobox"){label = _("my ") .. "combobox", value = 2, "8", "16", "32"}
|
local combobox = dt.new_widget("combobox"){label = _("MyCombobox"), value = 2, "8", "16", "32"}
|
||||||
|
|
||||||
-- https://www.darktable.org/lua-api/types_lua_entry.html
|
-- https://www.darktable.org/lua-api/types_lua_entry.html
|
||||||
local entry = dt.new_widget("entry")
|
local entry = dt.new_widget("entry")
|
||||||
@@ -122,21 +117,21 @@ local entry = dt.new_widget("entry")
|
|||||||
placeholder = _("placeholder"),
|
placeholder = _("placeholder"),
|
||||||
is_password = false,
|
is_password = false,
|
||||||
editable = true,
|
editable = true,
|
||||||
tooltip = _("tooltip text"),
|
tooltip = _("Tooltip Text"),
|
||||||
reset_callback = function(self) self.text = "text" end
|
reset_callback = function(self) self.text = "text" end
|
||||||
}
|
}
|
||||||
|
|
||||||
-- https://www.darktable.org/lua-api/types_lua_file_chooser_button.html
|
-- https://www.darktable.org/lua-api/types_lua_file_chooser_button.html
|
||||||
local file_chooser_button = dt.new_widget("file_chooser_button")
|
local file_chooser_button = dt.new_widget("file_chooser_button")
|
||||||
{
|
{
|
||||||
title = _("my ") .. "file_chooser_button", -- The title of the window when choosing a file
|
title = _("MyFile_chooser_button"), -- The title of the window when choosing a file
|
||||||
value = "", -- The currently selected file
|
value = "", -- The currently selected file
|
||||||
is_directory = false -- True if the file chooser button only allows directories to be selecte
|
is_directory = false -- True if the file chooser button only allows directories to be selecte
|
||||||
}
|
}
|
||||||
|
|
||||||
-- https://www.darktable.org/lua-api/types_lua_label.html
|
-- https://www.darktable.org/lua-api/types_lua_label.html
|
||||||
local label = dt.new_widget("label")
|
local label = dt.new_widget("label")
|
||||||
label.label = _("my ") .. "label" -- 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
|
-- https://www.darktable.org/lua-api/types_lua_separator.html
|
||||||
local separator = dt.new_widget("separator"){}
|
local separator = dt.new_widget("separator"){}
|
||||||
@@ -144,7 +139,7 @@ local separator = dt.new_widget("separator"){}
|
|||||||
-- https://www.darktable.org/lua-api/types_lua_slider.html
|
-- https://www.darktable.org/lua-api/types_lua_slider.html
|
||||||
local slider = dt.new_widget("slider")
|
local slider = dt.new_widget("slider")
|
||||||
{
|
{
|
||||||
label = _("my ") .. "slider",
|
label = _("MySlider"),
|
||||||
soft_min = 10, -- The soft minimum value for the slider, the slider can't go beyond this point
|
soft_min = 10, -- The soft minimum value for the slider, the slider can't go beyond this point
|
||||||
soft_max = 100, -- The soft maximum value for the slider, the slider can't go beyond this point
|
soft_max = 100, -- The soft maximum value for the slider, the slider can't go beyond this point
|
||||||
hard_min = 0, -- The hard minimum value for the slider, the user can't manually enter a value beyond this point
|
hard_min = 0, -- The hard minimum value for the slider, the user can't manually enter a value beyond this point
|
||||||
|
|||||||
@@ -29,7 +29,8 @@
|
|||||||
by hovering over the image and pressing the key combination.
|
by hovering over the image and pressing the key combination.
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||||
* exiftool - https://exiftool.org
|
* ufraw-batch - https://ufraw.sourceforge.net
|
||||||
|
MacOS - install with homebrew
|
||||||
|
|
||||||
USAGE
|
USAGE
|
||||||
* require this script from your main lua file
|
* require this script from your main lua file
|
||||||
@@ -43,7 +44,6 @@
|
|||||||
* Send to Bill Ferguson, wpferguson@gmail.com
|
* Send to Bill Ferguson, wpferguson@gmail.com
|
||||||
|
|
||||||
CHANGES
|
CHANGES
|
||||||
20251205 - replaced ufraw-batch with exiftool
|
|
||||||
]]
|
]]
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@@ -70,10 +70,13 @@ local dtsys = require "lib/dtutils.system" -- system utilities
|
|||||||
translations, inserting this lays the groundwork for anyone who wants to translate the strings.
|
translations, inserting this lays the groundwork for anyone who wants to translate the strings.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("multi_os",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext("multi_os", msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@@ -139,36 +142,6 @@ local function copy_image_attributes(from, to, ...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function check_file_size(img_name)
|
|
||||||
local result = 0
|
|
||||||
|
|
||||||
if dt.configuration.running_os == "windows" then
|
|
||||||
local path = df.get_path(img_name)
|
|
||||||
local fname = df.get_filename(img_name)
|
|
||||||
|
|
||||||
local p = io.open("forfiles /P " .. path .. " /M " .. fname .. " /C \"cmd /c echo $fsize\"")
|
|
||||||
|
|
||||||
if p then
|
|
||||||
for line in p:lines() do
|
|
||||||
if line:length() > 5 then
|
|
||||||
result = tonumber(line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
p:close()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local p = io.popen("ls -s " .. img_name)
|
|
||||||
|
|
||||||
if p then
|
|
||||||
local line = p:read("*a")
|
|
||||||
result = tonumber(du.split(line, " ")[1])
|
|
||||||
p:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
The main function called from the button or the shortcut. It takes one or more raw files, passed
|
The main function called from the button or the shortcut. It takes one or more raw files, passed
|
||||||
in a table and extracts the embedded jpeg images
|
in a table and extracts the embedded jpeg images
|
||||||
@@ -182,12 +155,11 @@ local function extract_embedded_jpeg(images)
|
|||||||
in the user's path. When it finds the executable, it returns the command to run it.
|
in the user's path. When it finds the executable, it returns the command to run it.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local exiftool_executable = df.check_if_bin_exists("exiftool")
|
local ufraw_executable = df.check_if_bin_exists("ufraw-batch")
|
||||||
if exiftool_executable then
|
if ufraw_executable then
|
||||||
for _, image in ipairs(images) do
|
for _, image in ipairs(images) do
|
||||||
if image.is_raw then
|
if image.is_raw then
|
||||||
local img_file = tostring(image)
|
local img_file = du.join({image.path, image.filename}, "/")
|
||||||
local jpg_img_file = string.gsub(img_file, "%....$", ".jpg")
|
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
dtsys.external_command() is operating system aware and formats the command as necessary.
|
dtsys.external_command() is operating system aware and formats the command as necessary.
|
||||||
@@ -195,12 +167,8 @@ local function extract_embedded_jpeg(images)
|
|||||||
operating system aware and uses the corresponding quotes.
|
operating system aware and uses the corresponding quotes.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
if dtsys.external_command(exiftool_executable .. " -b -JpgFromRaw " .. df.sanitize_filename(img_file) .. " > " .. df.sanitize_filename(jpg_img_file)) then
|
if dtsys.external_command(ufraw_executable .. " --silent --embedded-image " .. df.sanitize_filename(img_file)) then
|
||||||
dt.print_log("created jpg from raw")
|
local jpg_img_file = df.chop_filetype(img_file) .. ".embedded.jpg"
|
||||||
if check_file_size(df.sanitize_filename(jpg_img_file)) == 0 then
|
|
||||||
dt.print_log("using preview")
|
|
||||||
dtsys.external_command(exiftool_executable .. " -b -PreviewImage " .. df.sanitize_filename(img_file) .. " > " .. df.sanitize_filename(jpg_img_file))
|
|
||||||
end
|
|
||||||
dt.print_log("jpg_img_file set to ", jpg_img_file) -- print a debugging message
|
dt.print_log("jpg_img_file set to ", jpg_img_file) -- print a debugging message
|
||||||
local myimage = dt.database.import(jpg_img_file)
|
local myimage = dt.database.import(jpg_img_file)
|
||||||
myimage:group_with(image.group_leader)
|
myimage:group_with(image.group_leader)
|
||||||
@@ -219,12 +187,12 @@ local function extract_embedded_jpeg(images)
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
dt.print_error(image.filename .. " is not a raw file. No image can be extracted") -- print debugging error message
|
dt.print_error(image.filename .. " is not a raw file. No image can be extracted") -- print debugging error message
|
||||||
dt.print(string.format(_("%s is not a raw file, no image can be extracted"), image.filename)) -- print the error to the screen
|
dt.print(image.filename .. " is not a raw file. No image can be extracted") -- print the error to the screen
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
dt.print_error("exiftool not found. Exiting...") -- print debugging error message
|
dt.print_error("ufraw-batch not found. Exiting...") -- print debugging error message
|
||||||
dt.print("exiftool not found, exiting...") -- print the error to the screen
|
dt.print("ufraw-batch not found. Exiting...") -- print the error to the screen
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -248,7 +216,7 @@ end
|
|||||||
if dt.configuration.running_os ~= "linux" then
|
if dt.configuration.running_os ~= "linux" then
|
||||||
local executable = "ufraw-batch"
|
local executable = "ufraw-batch"
|
||||||
local ufraw_batch_path_widget = dt.new_widget("file_chooser_button"){
|
local ufraw_batch_path_widget = dt.new_widget("file_chooser_button"){
|
||||||
title = string.format(_("select %s executable"), "ufraw-batch[.exe]"),
|
title = _("Select ufraw-batch[.exe] executable"),
|
||||||
value = df.get_executable_path_preference(executable),
|
value = df.get_executable_path_preference(executable),
|
||||||
is_directory = false,
|
is_directory = false,
|
||||||
changed_callback = function(self)
|
changed_callback = function(self)
|
||||||
@@ -259,8 +227,8 @@ if dt.configuration.running_os ~= "linux" then
|
|||||||
}
|
}
|
||||||
dt.preferences.register("executable_paths", "ufraw-batch", -- name
|
dt.preferences.register("executable_paths", "ufraw-batch", -- name
|
||||||
"file", -- type
|
"file", -- type
|
||||||
'multi_os: ufraw-batch ' .. _('location'), -- label
|
_('multi_os: ufraw-batch location'), -- label
|
||||||
_('installed location of ufraw-batch, requires restart to take effect.'), -- tooltip
|
_('Installed location of ufraw-batch. Requires restart to take effect.'), -- tooltip
|
||||||
"ufraw-batch", -- default
|
"ufraw-batch", -- default
|
||||||
ufraw_batch_path_widget
|
ufraw_batch_path_widget
|
||||||
)
|
)
|
||||||
@@ -273,7 +241,7 @@ end
|
|||||||
dt.gui.libs.image.register_action(
|
dt.gui.libs.image.register_action(
|
||||||
"multi_os", _("extract embedded jpeg"),
|
"multi_os", _("extract embedded jpeg"),
|
||||||
function(event, images) extract_embedded_jpeg(images) end,
|
function(event, images) extract_embedded_jpeg(images) end,
|
||||||
_("extract embedded jpeg")
|
"extract embedded jpeg"
|
||||||
)
|
)
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@@ -283,7 +251,7 @@ dt.gui.libs.image.register_action(
|
|||||||
dt.register_event(
|
dt.register_event(
|
||||||
"multi_os", "shortcut",
|
"multi_os", "shortcut",
|
||||||
function(event, shortcut) extract_embedded_jpeg(dt.gui.action_images) end,
|
function(event, shortcut) extract_embedded_jpeg(dt.gui.action_images) end,
|
||||||
_("extract embedded jpeg")
|
"extract embedded jpeg"
|
||||||
)
|
)
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@@ -293,14 +261,6 @@ dt.register_event(
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("multi OS"),
|
|
||||||
purpose = _("example module thet runs on different operating systems"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/multi_os"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -53,16 +53,17 @@ end
|
|||||||
-- C O N S T A N T S
|
-- C O N S T A N T S
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
local MODULE_NAME = "panels_demo"
|
local MODULE_NAME = "panels"
|
||||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
-- T R A N S L A T I O N S
|
-- T R A N S L A T I O N S
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext(MODULE_NAME, msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@@ -93,29 +94,29 @@ dt.gui.panel_show_all()
|
|||||||
|
|
||||||
-- hide center_top, center_bottom, left, top, right, bottom in order
|
-- hide center_top, center_bottom, left, top, right, bottom in order
|
||||||
|
|
||||||
dt.print(_("hiding all panels, one at a time"))
|
dt.print(_("Hiding all panels, one at a tme"))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
|
|
||||||
for i = 1, #panels do
|
for i = 1, #panels do
|
||||||
dt.print(string.format(_("hiding %s"), panels[i]))
|
dt.print(_("Hiding " .. panels[i]))
|
||||||
dt.gui.panel_hide(panels[i])
|
dt.gui.panel_hide(panels[i])
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- display left, then top, then right, then bottom
|
-- display left, then top, then right, then bottom
|
||||||
|
|
||||||
dt.print(_("make panels visible, one at a time"))
|
dt.print(_("Make panels visible, one at a time"))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
|
|
||||||
for i = #panels, 1, -1 do
|
for i = #panels, 1, -1 do
|
||||||
dt.print(string.format(_("showing %s"), panels[i]))
|
dt.print(_("Showing " .. panels[i]))
|
||||||
dt.gui.panel_show(panels[i])
|
dt.gui.panel_show(panels[i])
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- hide all
|
-- hide all
|
||||||
|
|
||||||
dt.print(_("hiding all panels"))
|
dt.print(_("Hiding all panels"))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
|
|
||||||
dt.gui.panel_hide_all()
|
dt.gui.panel_hide_all()
|
||||||
@@ -123,7 +124,7 @@ sleep(1500)
|
|||||||
|
|
||||||
-- show all
|
-- show all
|
||||||
|
|
||||||
dt.print(_("showing all panels"))
|
dt.print(_("Showing all panels"))
|
||||||
sleep(1500)
|
sleep(1500)
|
||||||
|
|
||||||
dt.gui.panel_show_all()
|
dt.gui.panel_show_all()
|
||||||
@@ -131,7 +132,7 @@ sleep(1500)
|
|||||||
|
|
||||||
-- restore
|
-- restore
|
||||||
|
|
||||||
dt.print(_("restoring panels to starting configuration"))
|
dt.print(_("Restoring panels to starting configuration"))
|
||||||
for i = 1, #panels do
|
for i = 1, #panels do
|
||||||
if panel_status[i] then
|
if panel_status[i] then
|
||||||
dt.gui.panel_show(panels[i])
|
dt.gui.panel_show(panels[i])
|
||||||
@@ -144,14 +145,6 @@ end
|
|||||||
-- it's time to destroy the script and then return the data to
|
-- it's time to destroy the script and then return the data to
|
||||||
-- script_manager
|
-- script_manager
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("panels demo"),
|
|
||||||
purpose = _("example demonstrating how to contol panel visibility"),
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/panels_demo"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -24,45 +24,28 @@ USAGE
|
|||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
|
|
||||||
-- translation facilities
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("preference examples"),
|
|
||||||
purpose = _("example to show the different preference types that are possible"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/preferenceExamples"
|
|
||||||
}
|
|
||||||
|
|
||||||
du.check_min_api_version("2.0.1", "preferenceExamples")
|
du.check_min_api_version("2.0.1", "preferenceExamples")
|
||||||
|
|
||||||
|
|
||||||
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
||||||
"preferenceExamplesString", -- name
|
"preferenceExamplesString", -- name
|
||||||
"string", -- type
|
"string", -- type
|
||||||
_("example") .. " string", -- label
|
"Example String", -- label
|
||||||
_("example") .. " string " .. _("tooltip"), -- tooltip
|
"Example String Tooltip", -- tooltip
|
||||||
"String") -- default
|
"String") -- default
|
||||||
|
|
||||||
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
||||||
"preferenceExamplesBool", -- name
|
"preferenceExamplesBool", -- name
|
||||||
"bool", -- type
|
"bool", -- type
|
||||||
_("example") .. " boolean", -- label
|
"Example Boolean", -- label
|
||||||
_("example") .. " boolean " .. _("tooltip"), -- tooltip
|
"Example Boolean Tooltip", -- tooltip
|
||||||
true) -- default
|
true) -- default
|
||||||
|
|
||||||
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
||||||
"preferenceExamplesInteger", -- name
|
"preferenceExamplesInteger", -- name
|
||||||
"integer", -- type
|
"integer", -- type
|
||||||
_("example") .. " integer", -- label
|
"Example Integer", -- label
|
||||||
_("example") .. " integer " .. _("tooltip"), -- tooltip
|
"Example Integer Tooltip", -- tooltip
|
||||||
2, -- default
|
2, -- default
|
||||||
1, -- min
|
1, -- min
|
||||||
99) -- max
|
99) -- max
|
||||||
@@ -70,8 +53,8 @@ dt.preferences.register("preferenceExamples", -- script: This is a string
|
|||||||
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
||||||
"preferenceExamplesFloat", -- name
|
"preferenceExamplesFloat", -- name
|
||||||
"float", -- type
|
"float", -- type
|
||||||
_("example") .. " float", -- label
|
"Example Float", -- label
|
||||||
_("example") .. " float " .. _("tooltip"), -- tooltip
|
"Example Float Tooltip", -- tooltip
|
||||||
1.3, -- default
|
1.3, -- default
|
||||||
1, -- min
|
1, -- min
|
||||||
99, -- max
|
99, -- max
|
||||||
@@ -80,30 +63,22 @@ dt.preferences.register("preferenceExamples", -- script: This is a string
|
|||||||
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
||||||
"preferenceExamplesFile", -- name
|
"preferenceExamplesFile", -- name
|
||||||
"file", -- type
|
"file", -- type
|
||||||
_("example") .. " file", -- label
|
"Example File", -- label
|
||||||
_("example") .. " file " .. _("tooltip"), -- tooltip
|
"Example File Tooltip", -- tooltip
|
||||||
"") -- default
|
"") -- default
|
||||||
|
|
||||||
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
||||||
"preferenceExamplesDirectory", -- name
|
"preferenceExamplesDirectory", -- name
|
||||||
"directory", -- type
|
"directory", -- type
|
||||||
_("example") .. " directory", -- label
|
"Example Directory", -- label
|
||||||
_("example") .. " directory " .. _("tooltip"), -- tooltip
|
"Example Directory Tooltip", -- tooltip
|
||||||
"") -- default
|
"") -- default
|
||||||
|
|
||||||
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
dt.preferences.register("preferenceExamples", -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
||||||
"preferenceExamplesEnum", -- name
|
"preferenceExamplesEnum", -- name
|
||||||
"enum", -- type
|
"enum", -- type
|
||||||
_("example") .. " enum", -- label
|
"Example Enum", -- label
|
||||||
_("example") .. " enum " .. _("tooltip"), -- tooltip
|
"Example Enum Tooltip", -- tooltip
|
||||||
"Enum 1", -- default
|
"Enum 1", -- default
|
||||||
"Enum 1", "Enum 2") -- values
|
"Enum 1", "Enum 2") -- values
|
||||||
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
-- nothing to destroy
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
|
|||||||
@@ -24,14 +24,6 @@ local du = require "lib/dtutils"
|
|||||||
|
|
||||||
du.check_min_api_version("5.0.0", "printExamples")
|
du.check_min_api_version("5.0.0", "printExamples")
|
||||||
|
|
||||||
-- translation facilities
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- script_manager integration to allow a script to be removed
|
-- script_manager integration to allow a script to be removed
|
||||||
-- without restarting darktable
|
-- without restarting darktable
|
||||||
local function destroy()
|
local function destroy()
|
||||||
@@ -40,7 +32,7 @@ end
|
|||||||
|
|
||||||
-- Will print a string to the darktable control log (the long
|
-- Will print a string to the darktable control log (the long
|
||||||
-- overlayed window that appears over the main panel).
|
-- overlayed window that appears over the main panel).
|
||||||
dt.print(_("print"))
|
dt.print("print")
|
||||||
|
|
||||||
-- This function will print its parameter if the Lua logdomain is
|
-- This function will print its parameter if the Lua logdomain is
|
||||||
-- activated. Start darktable with the "-d lua" command line option
|
-- activated. Start darktable with the "-d lua" command line option
|
||||||
@@ -56,14 +48,6 @@ dt.print_log("print log")
|
|||||||
-- it's time to destroy the script and then return the data to
|
-- it's time to destroy the script and then return the data to
|
||||||
-- script_manager
|
-- script_manager
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("print examples"),
|
|
||||||
purpose = _("example showing the different types of printing messages"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/printExamples"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -30,34 +30,18 @@ local du = require "lib/dtutils"
|
|||||||
|
|
||||||
du.check_min_api_version("5.0.0", "running_os")
|
du.check_min_api_version("5.0.0", "running_os")
|
||||||
|
|
||||||
-- translation facilities
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- script_manager integration to allow a script to be removed
|
-- script_manager integration to allow a script to be removed
|
||||||
-- without restarting darktable
|
-- without restarting darktable
|
||||||
local function destroy()
|
local function destroy()
|
||||||
-- nothing to destroy
|
-- nothing to destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
dt.print(string.format(_("you are running: %s"), dt.configuration.running_os))
|
dt.print("You are running: "..dt.configuration.running_os)
|
||||||
|
|
||||||
-- set the destroy routine so that script_manager can call it when
|
-- set the destroy routine so that script_manager can call it when
|
||||||
-- it's time to destroy the script and then return the data to
|
-- it's time to destroy the script and then return the data to
|
||||||
-- script_manager
|
-- script_manager
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("running OS"),
|
|
||||||
purpose = _("example of how to determine the operating system being used"),
|
|
||||||
author = "Tobias Jakobs",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/running_os"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
return script_data
|
return script_data
|
||||||
|
|||||||
@@ -1,345 +1,163 @@
|
|||||||
--[[
|
--[[
|
||||||
This file is part of darktable,
|
This file is part of darktable,
|
||||||
copyright (c) 2023 Diederik ter Rahe
|
copyright (c) 2023 Diederik ter Rahe
|
||||||
|
|
||||||
darktable is free software: you can redistribute it and/or modify
|
darktable is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
darktable is distributed in the hope that it will be useful,
|
darktable is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
||||||
]]
|
]]
|
||||||
--[[
|
--[[
|
||||||
X-Touch Mini flexible encoder shortcuts
|
X-Touch Mini flexible encoder shortcuts
|
||||||
|
|
||||||
This script will create virtual sliders that are mapped dynamically to
|
This script will create virtual sliders that are mapped dynamically to
|
||||||
the most relevant sliders for the currently focused processing module.
|
the most relevant sliders for the currently focused processing module.
|
||||||
Tailored modules are color zones, tone equalizer, color calibration and
|
Tailored modules are color zones, tone equalizer, color calibration and
|
||||||
mask manager properties. The script can easily be amended for other
|
mask manager properties. The script can easily be amended for other
|
||||||
devices or personal preferences. Virtual "toggle" buttons can be created
|
devices or personal preferences. Virtual "toggle" buttons can be created
|
||||||
as well, that dynamically change meaning depending on current status.
|
as well, that dynamically change meaning depending on current status.
|
||||||
|
|
||||||
USAGE
|
USAGE
|
||||||
* require this script from your main lua file
|
* require this script from your main lua file
|
||||||
* restart darktable
|
* restart darktable
|
||||||
* create shortcuts for each of the encoders on the x-touch mini
|
* create shortcuts for each of the encoders on the x-touch mini
|
||||||
to a virtual slider under lua/x-touch
|
to a virtual slider under lua/x-touch
|
||||||
or import the following shortcutsrc file in the shortcuts dialog/preferences tab:
|
or import the following shortcutsrc file in the shortcuts dialog/preferences tab:
|
||||||
|
|
||||||
None;midi:CC1=lua/x-touch/knob 1
|
None;midi:CC1=lua/x-touch/knob 1
|
||||||
None;midi:CC2=lua/x-touch/knob 2
|
None;midi:CC2=lua/x-touch/knob 2
|
||||||
None;midi:CC3=lua/x-touch/knob 3
|
None;midi:CC3=lua/x-touch/knob 3
|
||||||
None;midi:CC4=lua/x-touch/knob 4
|
None;midi:CC4=lua/x-touch/knob 4
|
||||||
None;midi:CC5=lua/x-touch/knob 5
|
None;midi:CC5=lua/x-touch/knob 5
|
||||||
None;midi:CC6=lua/x-touch/knob 6
|
None;midi:CC6=lua/x-touch/knob 6
|
||||||
None;midi:CC7=lua/x-touch/knob 7
|
None;midi:CC7=lua/x-touch/knob 7
|
||||||
None;midi:CC8=lua/x-touch/knob 8
|
None;midi:CC8=lua/x-touch/knob 8
|
||||||
midi:E0=global/modifiers
|
midi:E0=global/modifiers
|
||||||
midi:F0=global/modifiers;ctrl
|
midi:F0=global/modifiers;ctrl
|
||||||
midi:F#0=global/modifiers;alt
|
midi:F#0=global/modifiers;alt
|
||||||
midi:G#-1=iop/blend/tools/show and edit mask elements
|
midi:G#-1=iop/blend/tools/show and edit mask elements
|
||||||
midi:A-1=iop/colorzones;focus
|
midi:A-1=iop/colorzones;focus
|
||||||
midi:A#-1=iop/toneequal;focus
|
midi:A#-1=iop/toneequal;focus
|
||||||
midi:B-1=iop/colorbalancergb;focus
|
midi:B-1=iop/colorbalancergb;focus
|
||||||
midi:C0=iop/channelmixerrgb;focus
|
midi:C0=iop/channelmixerrgb;focus
|
||||||
midi:C#0=iop/colorequal;focus
|
]]
|
||||||
midi:D0=iop/colorequal/page;previous
|
|
||||||
midi:D#0=iop/colorequal/page;next
|
local dt = require "darktable"
|
||||||
|
local du = require "lib/dtutils"
|
||||||
USAGE:
|
|
||||||
To add a new module, simply add an entry to MODULE_PROFILES below.
|
du.check_min_api_version("9.2.0", "x-touch")
|
||||||
Get action paths from darktable's shortcut interface.
|
|
||||||
]]
|
-- set up 8 mimic sliders with the same function
|
||||||
|
for k = 1,8 do
|
||||||
local dt = require "darktable"
|
dt.gui.mimic("slider", "knob ".. k,
|
||||||
local du = require "lib/dtutils"
|
function(action, element, effect, size)
|
||||||
|
-- take the number from the mimic name
|
||||||
du.check_min_api_version("9.2.0", "x-touch")
|
local k = tonumber(action:sub(-1))
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
-- only operate in darkroom; return NAN otherwise
|
||||||
local function _(msgid)
|
if dt.gui.current_view() ~= dt.gui.views.darkroom then
|
||||||
return gettext(msgid)
|
return 0/0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Constants
|
local maskval = 0/0
|
||||||
local NUM_KNOBS = 8
|
if k < 8 then
|
||||||
local NAN = 0/0
|
-- first try if the mask slider at that position is active
|
||||||
local darkroom_view = dt.gui.views.darkroom
|
local s = { "opacity",
|
||||||
|
"size",
|
||||||
-- ============================================================================
|
"feather",
|
||||||
-- USER CONFIGURATION
|
"hardness",
|
||||||
-- ============================================================================
|
"rotation",
|
||||||
|
"curvature",
|
||||||
-- Module Profiles: Define behavior when each module has focus
|
"compression" }
|
||||||
-- Structure: [module_name] = { knob1_action, knob2_action, ... }
|
maskval = dt.gui.action("lib/masks/properties/" .. s[k],
|
||||||
local MODULE_PROFILES = {
|
element, effect, size)
|
||||||
|
end
|
||||||
-- Color Zones: Control individual color channels
|
-- if a value different from NAN is returned, the slider was active
|
||||||
colorzones = {
|
if maskval == maskval then
|
||||||
{"iop/colorzones/graph", "red"},
|
return maskval
|
||||||
{"iop/colorzones/graph", "orange"},
|
|
||||||
{"iop/colorzones/graph", "yellow"},
|
-- try if colorzones module is focused; if so select element of graph
|
||||||
{"iop/colorzones/graph", "green"},
|
elseif dt.gui.action("iop/colorzones", "focus") ~= 0 then
|
||||||
{"iop/colorzones/graph", "aqua"},
|
which = "iop/colorzones/graph"
|
||||||
{"iop/colorzones/graph", "blue"},
|
local e = { "red",
|
||||||
{"iop/colorzones/graph", "purple"},
|
"orange",
|
||||||
{"iop/colorzones/graph", "magenta"}
|
"yellow",
|
||||||
},
|
"green",
|
||||||
|
"aqua",
|
||||||
-- Color Equalizer: Control color channels
|
"blue",
|
||||||
colorequal = {
|
"purple",
|
||||||
{"iop/colorequal/graph", "red"},
|
"magenta" }
|
||||||
{"iop/colorequal/graph", "orange"},
|
element = e[k]
|
||||||
{"iop/colorequal/graph", "yellow"},
|
|
||||||
{"iop/colorequal/graph", "green"},
|
|
||||||
{"iop/colorequal/graph", "cyan"},
|
-- if the sigmoid rgb primaries is focused,
|
||||||
{"iop/colorequal/graph", "blue"},
|
-- check sliders
|
||||||
{"iop/colorequal/graph", "lavender"},
|
elseif dt.gui.action("iop/sigmoid", "focus") ~= 0 and k <8 then
|
||||||
{"iop/colorequal/graph", "magenta"}
|
local e = { "red attenuation", "red rotation", "green attenuation", "green rotation", "blue attenuation", "blue rotation", "recover purity" }
|
||||||
},
|
which = "iop/sigmoid/primaries/"..e[k]
|
||||||
|
|
||||||
-- Sigmoid: Control primary color adjustments
|
-- if the rgb primaries is focused,
|
||||||
sigmoid = {
|
-- check sliders
|
||||||
{"iop/sigmoid/primaries/red attenuation"},
|
elseif dt.gui.action("iop/primaries", "focus") ~= 0 and k >=1 then
|
||||||
{"iop/sigmoid/primaries/red rotation"},
|
local e = { "red hue", "red purity", "green hue", "green purity", "blue hue", "blue purity", "tint hue", "tint purity" }
|
||||||
{"iop/sigmoid/primaries/green attenuation"},
|
which = "iop/primaries/" ..e[k]
|
||||||
{"iop/sigmoid/primaries/green rotation"},
|
|
||||||
{"iop/sigmoid/primaries/blue attenuation"},
|
-- if the tone equalizer is focused,
|
||||||
{"iop/sigmoid/primaries/blue rotation"},
|
-- select one of the sliders in the "simple" tab
|
||||||
{"iop/sigmoid/primaries/recover purity"}
|
elseif dt.gui.action("iop/toneequal", "focus") ~= 0 then
|
||||||
},
|
which ="iop/toneequal/simple/"..(k-9).." EV"
|
||||||
|
|
||||||
-- Color Calibration (Primaries)
|
-- if color calibration is focused,
|
||||||
primaries = {
|
-- the last 4 knobs are sent there
|
||||||
{"iop/primaries/red hue"},
|
elseif dt.gui.action("iop/channelmixerrgb", "focus") ~= 0
|
||||||
{"iop/primaries/red purity"},
|
and k >= 5 then
|
||||||
{"iop/primaries/green hue"},
|
-- knob 5 selects the active tab; pressing it resets to CAT
|
||||||
{"iop/primaries/green purity"},
|
if k == 5 then
|
||||||
{"iop/primaries/blue hue"},
|
which = "iop/channelmixerrgb/page"
|
||||||
{"iop/primaries/blue purity"},
|
element = "CAT"
|
||||||
{"iop/primaries/tint hue"},
|
-- since the tab action is not a slider,
|
||||||
{"iop/primaries/tint purity"}
|
-- the effects need to be translated
|
||||||
},
|
if effect == "up" then effect = "next"
|
||||||
|
elseif effect == "down" then effect = "previous"
|
||||||
-- AgX
|
else effect = "activate"
|
||||||
agx = {
|
end
|
||||||
{"iop/agx/white relative exposure"},
|
else
|
||||||
{"iop/agx/black relative exposure"},
|
-- knobs 6, 7 and 8 are for the three color sliders on each tab
|
||||||
{"iop/agx/curve/pivot relative exposure"},
|
which = "iop/focus/sliders"
|
||||||
{"iop/agx/curve/pivot target output"},
|
local e = { "1st",
|
||||||
{"iop/agx/curve/contrast"},
|
"2nd",
|
||||||
{"iop/agx/curve/shoulder power"},
|
"3rd" }
|
||||||
{"iop/agx/curve/toe power"},
|
element = e[k - 5]
|
||||||
{"iop/agx/curve/curve y gamma"}
|
end
|
||||||
},
|
|
||||||
|
-- the 4th knob is contrast;
|
||||||
-- Tone Equalizer: Control EV bands
|
-- either colorbalance if it is focused, or filmic
|
||||||
-- For 8 knobs, using offsets -8 to get bands
|
elseif dt.gui.action("iop/colorbalancergb", "focus") ~= 0
|
||||||
toneequal = {
|
and k == 4 then
|
||||||
{"iop/toneequal/simple/-8 EV"},
|
which = "iop/colorbalancergb/contrast"
|
||||||
{"iop/toneequal/simple/-7 EV"},
|
|
||||||
{"iop/toneequal/simple/-6 EV"},
|
-- in all other cases use a default selection
|
||||||
{"iop/toneequal/simple/-5 EV"},
|
else
|
||||||
{"iop/toneequal/simple/-4 EV"},
|
local s = { "iop/exposure/exposure",
|
||||||
{"iop/toneequal/simple/-3 EV"},
|
"iop/filmicrgb/white relative exposure",
|
||||||
{"iop/toneequal/simple/-2 EV"},
|
"iop/filmicrgb/black relative exposure",
|
||||||
{"iop/toneequal/simple/-1 EV"}
|
"iop/filmicrgb/contrast",
|
||||||
},
|
"iop/crop/left",
|
||||||
|
"iop/crop/right",
|
||||||
-- Color Balance RGB: Selected controls
|
"iop/crop/top",
|
||||||
colorbalancergb = {
|
"iop/crop/bottom" }
|
||||||
nil, -- Knob 1: unused --> default
|
which = s[k]
|
||||||
{"iop/colorbalancergb/global vibrance"},
|
end
|
||||||
{"iop/colorbalancergb/contrast"},
|
|
||||||
{"iop/colorbalancergb/global chroma"},
|
-- now pass the element/effect/size to the selected slider
|
||||||
{"iop/colorbalancergb/global saturation"},
|
return dt.gui.action(which, element, effect, size)
|
||||||
{"iop/colorbalancergb/global brilliance"},
|
end)
|
||||||
nil, -- Knob 7: --> default
|
end
|
||||||
nil -- Knob 8: --> default
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Channel Mixer RGB (Color Calibration): Tab control and sliders
|
|
||||||
channelmixerrgb = {
|
|
||||||
nil, -- Knob 1-4: --> default
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
{"iop/channelmixerrgb/page", "CAT", "tab"}, -- Knob 5: Tab selector
|
|
||||||
{"iop/focus/sliders", "1st"}, -- Knob 6: First slider
|
|
||||||
{"iop/focus/sliders", "2nd"}, -- Knob 7: Second slider
|
|
||||||
{"iop/focus/sliders", "3rd"} -- Knob 8: Third slider
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
local DEFAULT_MAPPINGS = {
|
|
||||||
{"iop/exposure/exposure"},
|
|
||||||
{"iop/filmicrgb/white relative exposure"},
|
|
||||||
{"iop/filmicrgb/black relative exposure"},
|
|
||||||
{"iop/filmicrgb/contrast"},
|
|
||||||
{"iop/crop/left"},
|
|
||||||
{"iop/crop/right"},
|
|
||||||
{"iop/crop/top"},
|
|
||||||
{"iop/crop/bottom"}
|
|
||||||
}
|
|
||||||
-- Mask properties: Always checked first, regardless of module focus
|
|
||||||
local MASK_PROPERTIES = {
|
|
||||||
{"lib/masks/properties/opacity"},
|
|
||||||
{"lib/masks/properties/size"},
|
|
||||||
{"lib/masks/properties/feather"},
|
|
||||||
{"lib/masks/properties/hardness"},
|
|
||||||
{"lib/masks/properties/rotation"},
|
|
||||||
{"lib/masks/properties/curvature"},
|
|
||||||
{"lib/masks/properties/compression"}
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Priority order for checking module focus (most commonly used first)
|
|
||||||
local MODULE_CHECK_ORDER = {
|
|
||||||
"toneequal",
|
|
||||||
"colorequal",
|
|
||||||
"colorbalancergb",
|
|
||||||
"agx",
|
|
||||||
"sigmoid",
|
|
||||||
"primaries",
|
|
||||||
"channelmixerrgb",
|
|
||||||
"colorzones"
|
|
||||||
}
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
-- CORE LOGIC - No need to edit below unless changing architecture
|
|
||||||
-- ============================================================================
|
|
||||||
|
|
||||||
-- Helper functions
|
|
||||||
local function is_in_darkroom()
|
|
||||||
return dt.gui.current_view() == darkroom_view
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_valid(value)
|
|
||||||
return value == value -- NaN check
|
|
||||||
end
|
|
||||||
|
|
||||||
local function translate_tab_effect(effect)
|
|
||||||
if effect == "up" then
|
|
||||||
return "next"
|
|
||||||
elseif effect == "down" then
|
|
||||||
return "previous"
|
|
||||||
else
|
|
||||||
return "activate"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Execute an action configuration
|
|
||||||
local function execute_action(action_config, element, effect, size)
|
|
||||||
if not action_config then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local path = action_config[1]
|
|
||||||
local elem = action_config[2] or element
|
|
||||||
local action_type = action_config[3]
|
|
||||||
|
|
||||||
-- Handle special action types
|
|
||||||
if action_type == "tab" then
|
|
||||||
effect = translate_tab_effect(effect)
|
|
||||||
end
|
|
||||||
|
|
||||||
return dt.gui.action(path, elem, effect, size)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Try mask properties first
|
|
||||||
local function try_mask_properties(knob_idx, element, effect, size)
|
|
||||||
if knob_idx >= NUM_KNOBS then
|
|
||||||
return NAN
|
|
||||||
end
|
|
||||||
|
|
||||||
local mask_action = MASK_PROPERTIES[knob_idx]
|
|
||||||
if not mask_action then
|
|
||||||
return NAN
|
|
||||||
end
|
|
||||||
|
|
||||||
local value = execute_action(mask_action, element, effect, size)
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Main knob handler
|
|
||||||
local function handle_knob_action(knob_idx, element, effect, size)
|
|
||||||
-- Early exit for non-darkroom view
|
|
||||||
if not is_in_darkroom() then
|
|
||||||
return NAN
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Try mask properties first (no focus check needed)
|
|
||||||
local mask_value = try_mask_properties(knob_idx, element, effect, size)
|
|
||||||
if is_valid(mask_value) then
|
|
||||||
return mask_value
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check focused modules in priority order
|
|
||||||
for _, module_name in ipairs(MODULE_CHECK_ORDER) do
|
|
||||||
local focus_status = dt.gui.action("iop/" .. module_name, "focus")
|
|
||||||
|
|
||||||
if focus_status ~= 0 then
|
|
||||||
-- Module has focus, get its profile
|
|
||||||
local profile = MODULE_PROFILES[module_name]
|
|
||||||
|
|
||||||
if profile then
|
|
||||||
local action_config = profile[knob_idx]
|
|
||||||
|
|
||||||
-- If profile has explicit mapping for this knob, use it
|
|
||||||
if action_config then
|
|
||||||
local result = execute_action(action_config, element, effect, size)
|
|
||||||
if result then
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Profile exists but this knob is nil -> use default
|
|
||||||
-- This avoids checking other modules when we know the focused one
|
|
||||||
local default_action = DEFAULT_MAPPINGS[knob_idx]
|
|
||||||
return execute_action(default_action, element, effect, size) or NAN
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If no profile exists for this module, continue checking others
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- No module has focus or no profile matched, fall back to default
|
|
||||||
local default_action = DEFAULT_MAPPINGS[knob_idx]
|
|
||||||
return execute_action(default_action, element, effect, size) or NAN
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Setup mimic sliders
|
|
||||||
local function setup_knobs()
|
|
||||||
for k = 1, NUM_KNOBS do
|
|
||||||
dt.gui.mimic("slider", "knob " .. k,
|
|
||||||
function(action, element, effect, size)
|
|
||||||
local knob_idx = tonumber(action:sub(-1))
|
|
||||||
return handle_knob_action(knob_idx, element, effect, size)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Script metadata
|
|
||||||
local script_data = {
|
|
||||||
metadata = {
|
|
||||||
name = _("x-touch"),
|
|
||||||
purpose = _("data-driven x-touch midi controller configuration"),
|
|
||||||
author = "Martin Straeten",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/examples/x-touch"
|
|
||||||
},
|
|
||||||
destroy = function() end,
|
|
||||||
destroy_method = nil,
|
|
||||||
restart = nil,
|
|
||||||
show = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Initialize
|
|
||||||
setup_knobs()
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ dtutils.libdoc.functions["check_min_api_version"] = {
|
|||||||
|
|
||||||
function dtutils.check_min_api_version(min_api, script_name)
|
function dtutils.check_min_api_version(min_api, script_name)
|
||||||
local current_api = dt.configuration.api_version_string
|
local current_api = dt.configuration.api_version_string
|
||||||
if dtutils.compare_api_versions(min_api, current_api) > 0 then
|
if min_api > current_api then
|
||||||
dt.print_error("This application is written for lua api version " .. min_api .. " or later.")
|
dt.print_error("This application is written for lua api version " .. min_api .. " or later.")
|
||||||
dt.print_error("The current lua api version is " .. current_api)
|
dt.print_error("The current lua api version is " .. current_api)
|
||||||
dt.print("ERROR: " .. script_name .. " failed to load. Lua API version " .. min_api .. " or later required.")
|
dt.print("ERROR: " .. script_name .. " failed to load. Lua API version " .. min_api .. " or later required.")
|
||||||
@@ -82,7 +82,8 @@ dtutils.libdoc.functions["check_max_api_version"] = {
|
|||||||
run against the current api version. This function is used when a part of the Lua API that
|
run against the current api version. This function is used when a part of the Lua API that
|
||||||
the script relies on is removed. If the maximum api version is not met, then an
|
the script relies on is removed. If the maximum api version is not met, then an
|
||||||
error message is printed saying the script_name failed to load, then an error is thrown causing the
|
error message is printed saying the script_name failed to load, then an error is thrown causing the
|
||||||
program to stop executing.]],
|
program to stop executing.
|
||||||
|
|
||||||
Return_Value = [[result - true if the maximum api version is available, false if not.]],
|
Return_Value = [[result - true if the maximum api version is available, false if not.]],
|
||||||
Limitations = [[When using the default handler on a script being executed from the luarc file, the error thrown
|
Limitations = [[When using the default handler on a script being executed from the luarc file, the error thrown
|
||||||
will stop the luarc file from executing any remaining statements. This limitation does not apply to script_manger.]],
|
will stop the luarc file from executing any remaining statements. This limitation does not apply to script_manger.]],
|
||||||
@@ -96,7 +97,7 @@ dtutils.libdoc.functions["check_max_api_version"] = {
|
|||||||
|
|
||||||
function dtutils.check_max_api_version(max_api, script_name)
|
function dtutils.check_max_api_version(max_api, script_name)
|
||||||
local current_api = dt.configuration.api_version_string
|
local current_api = dt.configuration.api_version_string
|
||||||
if dtutils.compare_api_versions(current_api, max_api) > 0 then
|
if current_api > max_api then
|
||||||
dt.print_error("This application is written for lua api version " .. max_api .. " or earlier.")
|
dt.print_error("This application is written for lua api version " .. max_api .. " or earlier.")
|
||||||
dt.print_error("The current lua api version is " .. current_api)
|
dt.print_error("The current lua api version is " .. current_api)
|
||||||
dt.print("ERROR: " .. script_name .. " failed to load. Lua API version " .. max_api .. " or earlier required.")
|
dt.print("ERROR: " .. script_name .. " failed to load. Lua API version " .. max_api .. " or earlier required.")
|
||||||
@@ -374,7 +375,7 @@ dtutils.libdoc.functions["deprecated"] = {
|
|||||||
|
|
||||||
du.deprecated(script_name, removal_string)
|
du.deprecated(script_name, removal_string)
|
||||||
script_name - name of the script being deprecated
|
script_name - name of the script being deprecated
|
||||||
removal_string - a string explaining when the script will be removed]],
|
removal_strubg - a string explaining when the script will be removed]],
|
||||||
Description = [[deprecated prints an error message saying the script is deprecated and when it will be removed]],
|
Description = [[deprecated prints an error message saying the script is deprecated and when it will be removed]],
|
||||||
Return_Value = [[]],
|
Return_Value = [[]],
|
||||||
Limitations = [[]],
|
Limitations = [[]],
|
||||||
@@ -391,77 +392,5 @@ function dtutils.deprecated(script_name, removal_string)
|
|||||||
dt.print_error("WARNING: " .. script_name .. " is deprecated and will be removed in " .. removal_string)
|
dt.print_error("WARNING: " .. script_name .. " is deprecated and will be removed in " .. removal_string)
|
||||||
end
|
end
|
||||||
|
|
||||||
dtutils.libdoc.functions["gen_uuid"] = {
|
|
||||||
Name = [[gen_uuid]],
|
|
||||||
Synopsis = [[generate a UUID string]],
|
|
||||||
Usage = [[local du = require "lib/dtutils"
|
|
||||||
|
|
||||||
uuid = du.gen_uuid(case)
|
|
||||||
case - "upper" or "lower" to specify the case of the UUID string]],
|
|
||||||
Description = [[gen_uuid prints an error message saying the script is gen_uuid and when it will be removed]],
|
|
||||||
Return_Value = [[uuid - string - a hexidecimal string representing the UUID in the requested case]],
|
|
||||||
Limitations = [[]],
|
|
||||||
Example = [[]],
|
|
||||||
See_Also = [[]],
|
|
||||||
Reference = [[https://gist.github.com/jrus/3197011]],
|
|
||||||
License = [[]],
|
|
||||||
Copyright = [[]],
|
|
||||||
}
|
|
||||||
|
|
||||||
function dtutils.gen_uuid(case)
|
|
||||||
local template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
|
||||||
|
|
||||||
-- seed with os.time in seconds and add an extra degree of random for multiple calls in the same second
|
|
||||||
math.randomseed(os.time(), math.random(0, 65536))
|
|
||||||
|
|
||||||
local uuid = string.gsub(template, '[xy]', function (c)
|
|
||||||
local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
|
|
||||||
return string.format('%x', v)
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
if case and case == "upper" then
|
|
||||||
uuid = string.upper(uuid)
|
|
||||||
end
|
|
||||||
|
|
||||||
return uuid
|
|
||||||
end
|
|
||||||
|
|
||||||
dtutils.libdoc.functions["compare_api_versions"] = {
|
|
||||||
Name = [[compare_api_versions]],
|
|
||||||
Synopsis = [[compare two API version strings]],
|
|
||||||
Usage = [[local du = require "lib/dtutils"
|
|
||||||
|
|
||||||
local result = du.compare_api_versions(version1, version2)
|
|
||||||
version1 - string - the first version string to compare (example: "5.0.0")
|
|
||||||
version2 - string - the second version string to compare (example: "5.1.0")]],
|
|
||||||
Description = [[compare_api_versions compares two version strings and returns 1 if version1 is greater,
|
|
||||||
-1 if version2 is greater, and 0 if they are equal.]],
|
|
||||||
Return_Value = [[result - 1 if version1 is greater, -1 if version2 is greater, 0 if they are equal.]],
|
|
||||||
Limitations = [[]],
|
|
||||||
Example = [[compare_api_versions("5.0.0", "5.1.0") returns -1]],
|
|
||||||
See_Also = [[]],
|
|
||||||
Reference = [[]],
|
|
||||||
License = [[]],
|
|
||||||
Copyright = [[]],
|
|
||||||
}
|
|
||||||
|
|
||||||
function dtutils.compare_api_versions(version1, version2)
|
|
||||||
local v1 = {}
|
|
||||||
for num in version1:gmatch("%d+") do table.insert(v1, tonumber(num)) end
|
|
||||||
local v2 = {}
|
|
||||||
for num in version2:gmatch("%d+") do table.insert(v2, tonumber(num)) end
|
|
||||||
|
|
||||||
for i = 1, math.max(#v1, #v2) do
|
|
||||||
local num1 = v1[i] or 0
|
|
||||||
local num2 = v2[i] or 0
|
|
||||||
if num1 > num2 then
|
|
||||||
return 1
|
|
||||||
elseif num1 < num2 then
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
return dtutils
|
return dtutils
|
||||||
|
|||||||
@@ -24,12 +24,15 @@ dtutils_file.libdoc = {
|
|||||||
functions = {}
|
functions = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("5.0.0", "dtutils.file")
|
du.check_min_api_version("5.0.0", "dtutils.file")
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("dtutils.file",dt.configuration.config_dir.."/lua/locale/")
|
||||||
|
|
||||||
local function _(msgid)
|
local function _(msgid)
|
||||||
return gettext(msgid)
|
return gettext.dgettext("dtutils.file", msgid)
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@@ -519,9 +522,9 @@ function dtutils_file.file_copy(fromFile, toFile)
|
|||||||
local result = nil
|
local result = nil
|
||||||
-- if cp exists, use it
|
-- if cp exists, use it
|
||||||
if dt.configuration.running_os == "windows" then
|
if dt.configuration.running_os == "windows" then
|
||||||
result = os.execute('copy ' .. dtutils_file.sanitize_filename(fromFile) .. ' ' .. dtutils_file.sanitize_filename(toFile))
|
result = os.execute('copy "' .. fromFile .. '" "' .. toFile .. '"')
|
||||||
elseif dtutils_file.check_if_bin_exists("cp") then
|
elseif dtutils_file.check_if_bin_exists("cp") then
|
||||||
result = os.execute("cp " .. dtutils_file.sanitize_filename(fromFile) .. ' ' .. dtutils_file.sanitize_filename(toFile))
|
result = os.execute("cp '" .. fromFile .. "' '" .. toFile .. "'")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if cp was not present, or if cp failed, then a pure lua solution
|
-- if cp was not present, or if cp failed, then a pure lua solution
|
||||||
@@ -572,7 +575,7 @@ function dtutils_file.file_move(fromFile, toFile)
|
|||||||
if not success then
|
if not success then
|
||||||
-- an error occurred, so let's try using the operating system function
|
-- an error occurred, so let's try using the operating system function
|
||||||
if dtutils_file.check_if_bin_exists("mv") then
|
if dtutils_file.check_if_bin_exists("mv") then
|
||||||
success = os.execute("mv " .. dtutils_file.sanitize_filename(fromFile) .. ' ' .. dtutils_file.sanitize_filename(toFile))
|
success = os.execute("mv '" .. fromFile .. "' '" .. toFile .. "'")
|
||||||
end
|
end
|
||||||
-- if the mv didn't exist or succeed, then...
|
-- if the mv didn't exist or succeed, then...
|
||||||
if not success then
|
if not success then
|
||||||
@@ -812,7 +815,7 @@ dtutils_file.libdoc.functions["mkdir"] = {
|
|||||||
function dtutils_file.mkdir(path)
|
function dtutils_file.mkdir(path)
|
||||||
if not dtutils_file.check_if_file_exists(path) then
|
if not dtutils_file.check_if_file_exists(path) then
|
||||||
local mkdir_cmd = dt.configuration.running_os == "windows" and "mkdir" or "mkdir -p"
|
local mkdir_cmd = dt.configuration.running_os == "windows" and "mkdir" or "mkdir -p"
|
||||||
return dsys.external_command(mkdir_cmd.." "..dtutils_file.sanitize_filename(path))
|
return dsys.external_command(mkdir_cmd.." "..path)
|
||||||
else
|
else
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
@@ -837,7 +840,7 @@ dtutils_file.libdoc.functions["rmdir"] = {
|
|||||||
|
|
||||||
function dtutils_file.rmdir(path)
|
function dtutils_file.rmdir(path)
|
||||||
local rm_cmd = dt.configuration.running_os == "windows" and "rmdir /S /Q" or "rm -r"
|
local rm_cmd = dt.configuration.running_os == "windows" and "rmdir /S /Q" or "rm -r"
|
||||||
return dsys.external_command(rm_cmd.." "..dtutils_file.sanitize_filename(path))
|
return dsys.external_command(rm_cmd.." "..path)
|
||||||
end
|
end
|
||||||
|
|
||||||
dtutils_file.libdoc.functions["create_tmp_file"] = {
|
dtutils_file.libdoc.functions["create_tmp_file"] = {
|
||||||
@@ -858,6 +861,9 @@ dtutils_file.libdoc.functions["create_tmp_file"] = {
|
|||||||
|
|
||||||
function dtutils_file.create_tmp_file()
|
function dtutils_file.create_tmp_file()
|
||||||
local tmp_file = os.tmpname()
|
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")
|
local f = io.open(tmp_file, "w")
|
||||||
if not f then
|
if not f then
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ function dtutils_log.msg(level, ...)
|
|||||||
table.remove(args, 1)
|
table.remove(args, 1)
|
||||||
end
|
end
|
||||||
local log_msg = level.label
|
local log_msg = level.label
|
||||||
if level.engine ~= dt_print and call_level ~= 0 then
|
if level.engine ~= dt_screen and call_level ~= 0 then
|
||||||
log_msg = log_msg .. dtutils_log.caller(call_level, level.caller_info) .. " "
|
log_msg = log_msg .. dtutils_log.caller(call_level, level.caller_info) .. " "
|
||||||
elseif log_msg:len() > 2 then
|
elseif log_msg:len() > 2 then
|
||||||
log_msg = log_msg .. " "
|
log_msg = log_msg .. " "
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
|||||||
local dtutils_system = {}
|
local dtutils_system = {}
|
||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local ds = require "lib/dtutils.string"
|
|
||||||
|
|
||||||
dtutils_system.libdoc = {
|
dtutils_system.libdoc = {
|
||||||
Name = [[dtutils.system]],
|
Name = [[dtutils.system]],
|
||||||
@@ -86,15 +85,8 @@ function dtutils_system.windows_command(command)
|
|||||||
local file = io.open(fname, "w")
|
local file = io.open(fname, "w")
|
||||||
if file then
|
if file then
|
||||||
dt.print_log("opened file")
|
dt.print_log("opened file")
|
||||||
file:write("@echo off\n")
|
command = string.gsub(command, "%%", "%%%%") -- escape % from windows shell
|
||||||
file:write('for /f "tokens=2 delims=:." %%x in (\'chcp\') do set cp=%%x\n')
|
file:write(command)
|
||||||
file:write("chcp 65001>nul\n") -- change the encoding of the terminal to handle non-english characters in path
|
|
||||||
file:write("\n")
|
|
||||||
file:write(command .. "\n")
|
|
||||||
file:write("set COMMAND_RC=%ERRORLEVEL%")
|
|
||||||
file:write("\n")
|
|
||||||
file:write("chcp %cp%>nul\n")
|
|
||||||
file:write("exit /b %COMMAND_RC%") -- otherwise, when the real command fails, script will still return "good" error code of chcp
|
|
||||||
file:close()
|
file:close()
|
||||||
|
|
||||||
result = dt.control.execute(fname)
|
result = dt.control.execute(fname)
|
||||||
@@ -126,7 +118,6 @@ dtutils_system.libdoc.functions["launch_default_app"] = {
|
|||||||
License = [[]],
|
License = [[]],
|
||||||
Copyright = [[]],
|
Copyright = [[]],
|
||||||
}
|
}
|
||||||
|
|
||||||
function dtutils_system.launch_default_app(path)
|
function dtutils_system.launch_default_app(path)
|
||||||
local open_cmd = "xdg-open "
|
local open_cmd = "xdg-open "
|
||||||
if (dt.configuration.running_os == "windows") then
|
if (dt.configuration.running_os == "windows") then
|
||||||
@@ -137,4 +128,5 @@ function dtutils_system.launch_default_app(path)
|
|||||||
return dtutils_system.external_command(open_cmd .. path)
|
return dtutils_system.external_command(open_cmd .. path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return dtutils_system
|
return dtutils_system
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ msgstr ""
|
|||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"Language: \n"
|
"Language: \n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=CHARSET\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 1.5.4\n"
|
"X-Generator: Poedit 1.5.4\n"
|
||||||
"Language: de_DE\n"
|
"Language: de_DE\n"
|
||||||
|
|||||||
@@ -1,514 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
apply_camera_style.lua - apply camera style to matching images
|
|
||||||
|
|
||||||
Copyright (C) 2024 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
apply_camera_style - apply darktable camera style to matching images
|
|
||||||
|
|
||||||
apply a camera style corresponding to the camera used to
|
|
||||||
take the image to provide a starting point for editing that
|
|
||||||
is similar to the SOOC jpeg.
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
none
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
start the script from script_manager
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
Bill Ferguson <wpferguson@gmail.com>
|
|
||||||
|
|
||||||
CHANGES
|
|
||||||
|
|
||||||
20251202 - fixed pattern matching bugs affecting Canon EOS R? series
|
|
||||||
styles and styles being applied twice if images are reimported
|
|
||||||
|
|
||||||
Added more debugging statements to pattern matching functions
|
|
||||||
]]
|
|
||||||
|
|
||||||
local dt = require "darktable"
|
|
||||||
local du = require "lib/dtutils"
|
|
||||||
-- local df = require "lib/dtutils.file"
|
|
||||||
local ds = require "lib/dtutils.string"
|
|
||||||
-- local dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "apply_camera_style"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.warn
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
local STYLE_PREFIX <const> = "_l10n_darktable|_l10n_camera styles|"
|
|
||||||
local MAKER = 3
|
|
||||||
local STYLE = 4
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("9.4.0", MODULE) -- camera styles added to darktable 5.0
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("apply camera style"), -- name of script
|
|
||||||
purpose = _("apply darktable camera style to matching images"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/development/lua.scripts.manual/scripts/official/apply_camera_style/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local apply_camera_style = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
apply_camera_style.imported_images = {}
|
|
||||||
apply_camera_style.styles = {}
|
|
||||||
apply_camera_style.log_level = DEFAULT_LOG_LEVEL
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = apply_camera_style
|
|
||||||
local acs = apply_camera_style
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
-- helper functions
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
local function set_log_level(level)
|
|
||||||
local old_log_level = log.log_level()
|
|
||||||
log.log_level(level)
|
|
||||||
return old_log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_log_level(level)
|
|
||||||
log.log_level(level)
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
-- script functions
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
local function process_pattern(pattern)
|
|
||||||
|
|
||||||
local log_level = set_log_level(acs.log_level)
|
|
||||||
|
|
||||||
log.msg(log.debug, "input pattern is " .. pattern)
|
|
||||||
|
|
||||||
pattern = string.lower(pattern)
|
|
||||||
log.msg(log.debug, "lower case pattern is " .. pattern)
|
|
||||||
-- strip off series
|
|
||||||
pattern = string.gsub(pattern, " series$", "?")
|
|
||||||
log.msg(log.debug, "series stripped off pattern is " .. pattern)
|
|
||||||
-- match a character
|
|
||||||
if string.match(pattern, "r%?%?$") then
|
|
||||||
-- handle EOS R case
|
|
||||||
pattern = string.gsub(pattern, "%?%?", ".?")
|
|
||||||
log.msg(log.debug, "matched EOS R pattern is " .. pattern)
|
|
||||||
else
|
|
||||||
pattern = string.gsub(pattern, "?", ".")
|
|
||||||
log.msg(log.debug, "didn't match EOS R pattern is " .. pattern)
|
|
||||||
end
|
|
||||||
-- escape dashes
|
|
||||||
pattern = string.gsub(pattern, "%-", "%%-")
|
|
||||||
log.msg(log.debug, "escape dashes conditional pattern is " .. pattern)
|
|
||||||
-- make spaces optional
|
|
||||||
pattern = string.gsub(pattern, " ", " ?")
|
|
||||||
-- until we end up with a set, I'll defer set processing, i.e. [...]
|
|
||||||
--log.msg(log.debug, "make spaces conditional again? pattern is " .. pattern)
|
|
||||||
-- anchor the pattern to ensure we don't short match
|
|
||||||
pattern = "^" .. pattern .. "$"
|
|
||||||
log.msg(log.debug, "anchored pattern is " .. pattern)
|
|
||||||
|
|
||||||
restore_log_level(log_level)
|
|
||||||
|
|
||||||
return pattern
|
|
||||||
end
|
|
||||||
|
|
||||||
local function process_set(pattern_set)
|
|
||||||
|
|
||||||
local log_level = set_log_level(acs.log_level)
|
|
||||||
|
|
||||||
local to_process = {}
|
|
||||||
local processed = {}
|
|
||||||
|
|
||||||
local base, set, tail
|
|
||||||
|
|
||||||
if string.match(pattern_set, "]$") then
|
|
||||||
base, set = string.match(pattern_set, "(.+)%[(.+)%]")
|
|
||||||
else
|
|
||||||
base, set, tail = string.match(pattern_set, "(.+)%[(.+)%](.+)")
|
|
||||||
end
|
|
||||||
|
|
||||||
log.msg(log.debug, "base is " .. base .. " and set is " .. set)
|
|
||||||
|
|
||||||
to_process = du.split(set, ",")
|
|
||||||
|
|
||||||
for _, item in ipairs(to_process) do
|
|
||||||
local pat = base .. item
|
|
||||||
if tail then
|
|
||||||
pat = pat .. tail
|
|
||||||
end
|
|
||||||
table.insert(processed, process_pattern(pat))
|
|
||||||
end
|
|
||||||
|
|
||||||
restore_log_level(log_level)
|
|
||||||
|
|
||||||
return processed
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_camera_styles()
|
|
||||||
|
|
||||||
local log_level = set_log_level(acs.log_level)
|
|
||||||
|
|
||||||
-- separate the styles into
|
|
||||||
--
|
|
||||||
-- acs.styles -
|
|
||||||
-- maker -
|
|
||||||
-- styles {}
|
|
||||||
-- patterns {}
|
|
||||||
|
|
||||||
for _, style in ipairs(dt.styles) do
|
|
||||||
|
|
||||||
if string.match(style.name, STYLE_PREFIX) then
|
|
||||||
log.msg(log.debug, "got " .. style.name)
|
|
||||||
|
|
||||||
local parts = du.split(style.name, "|")
|
|
||||||
parts[MAKER] = string.lower(parts[MAKER])
|
|
||||||
log.msg(log.debug, "maker is " .. parts[MAKER])
|
|
||||||
|
|
||||||
if not acs.styles[parts[MAKER]] then
|
|
||||||
acs.styles[parts[MAKER]] = {}
|
|
||||||
acs.styles[parts[MAKER]]["styles"] = {}
|
|
||||||
acs.styles[parts[MAKER]]["patterns"] = {}
|
|
||||||
end
|
|
||||||
if parts[STYLE] then
|
|
||||||
if not string.match(parts[STYLE], "]") then
|
|
||||||
table.insert(acs.styles[parts[MAKER]].styles, style)
|
|
||||||
local processed_pattern = process_pattern(parts[#parts])
|
|
||||||
table.insert(acs.styles[parts[MAKER]].patterns, processed_pattern)
|
|
||||||
log.msg(log.debug, "pattern for " .. style.name .. " is " .. processed_pattern)
|
|
||||||
else
|
|
||||||
local processed_patterns = process_set(parts[STYLE])
|
|
||||||
for _, pat in ipairs(processed_patterns) do
|
|
||||||
table.insert(acs.styles[parts[MAKER]].styles, style)
|
|
||||||
table.insert(acs.styles[parts[MAKER]].patterns, pat)
|
|
||||||
log.msg(log.debug, "pattern for " .. style.name .. " is " .. pat)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
restore_log_level(log_level)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
local function normalize_model(maker, model)
|
|
||||||
|
|
||||||
local log_level = set_log_level(acs.log_level)
|
|
||||||
|
|
||||||
model = string.lower(model)
|
|
||||||
|
|
||||||
-- strip off the maker name
|
|
||||||
if maker == "canon" then
|
|
||||||
model = string.gsub(model, "canon ", "")
|
|
||||||
elseif maker == "hasselblad" then
|
|
||||||
model = string.gsub(model, "hasselblad ", "")
|
|
||||||
elseif maker == "leica" then
|
|
||||||
model = string.gsub(model, "leica ", "")
|
|
||||||
elseif maker == "lg" then
|
|
||||||
model = string.gsub(model, "lg ", "")
|
|
||||||
elseif maker == "nikon" then
|
|
||||||
model = string.gsub(model, "nikon ", "")
|
|
||||||
elseif maker == "nokia" then
|
|
||||||
model = string.gsub(model, "nokia ", "")
|
|
||||||
elseif maker == "oneplus" then
|
|
||||||
model = string.gsub(model, "oneplus ", "")
|
|
||||||
elseif maker == "pentax" then
|
|
||||||
model = string.gsub(model, "pentax ", "")
|
|
||||||
model = string.gsub(model, "ricoh ", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
restore_log_level(log_level)
|
|
||||||
|
|
||||||
return model
|
|
||||||
end
|
|
||||||
|
|
||||||
local function normalize_maker(maker)
|
|
||||||
|
|
||||||
local log_level = set_log_level(acs.log_level)
|
|
||||||
|
|
||||||
maker = string.lower(maker)
|
|
||||||
|
|
||||||
if string.match(maker, "^fujifilm") then
|
|
||||||
maker = "fujifilm"
|
|
||||||
elseif string.match(maker, "^hmd ") then
|
|
||||||
maker = "nokia"
|
|
||||||
elseif string.match(maker, "^leica") then
|
|
||||||
maker = "leica"
|
|
||||||
elseif string.match(maker, "^minolta") then
|
|
||||||
maker = "minolta"
|
|
||||||
elseif string.match(maker, "^nikon") then
|
|
||||||
maker = "nikon"
|
|
||||||
elseif string.match(maker, "^om ") then
|
|
||||||
maker = "om system"
|
|
||||||
elseif string.match(maker, "^olympus") then
|
|
||||||
maker = "olympus"
|
|
||||||
elseif string.match(maker, "^pentax") or string.match(maker, "^ricoh") then
|
|
||||||
maker = "pentax"
|
|
||||||
end
|
|
||||||
|
|
||||||
restore_log_level(log_level)
|
|
||||||
|
|
||||||
return maker
|
|
||||||
end
|
|
||||||
|
|
||||||
local function has_style_tag(image, tag_name)
|
|
||||||
|
|
||||||
local log_level = set_log_level(acs.log_level)
|
|
||||||
|
|
||||||
local result = false
|
|
||||||
|
|
||||||
log.msg(log.debug, "looking for tag " .. tag_name)
|
|
||||||
|
|
||||||
for _, tag in ipairs(image:get_tags()) do
|
|
||||||
log.msg(log.debug, "got attached tag " .. tag.name)
|
|
||||||
local cleaned_tag_name = string.gsub(tag_name, "_l10n_", "")
|
|
||||||
log.msg(log.debug, "checking against " .. cleaned_tag_name)
|
|
||||||
if ds.sanitize_lua(tag.name) == cleaned_tag_name then
|
|
||||||
log.msg(log.debug, "matched tag " .. cleaned_tag_name)
|
|
||||||
result = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
restore_log_level(log_level)
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
local function mangle_model(model)
|
|
||||||
|
|
||||||
local log_level = set_log_level(acs.log_level)
|
|
||||||
|
|
||||||
if string.match(model, "eos") then
|
|
||||||
log.msg(log.debug, "mangle model got " .. model)
|
|
||||||
model = string.gsub(model, "r6m2", "r6 mark ii")
|
|
||||||
model = string.gsub(model, "eos 350d digital", "eos kiss digital n")
|
|
||||||
model = string.gsub(model, "eos 500d", "eos rebel t1")
|
|
||||||
model = string.gsub(model, "eos 550d", "eos rebel t2")
|
|
||||||
model = string.gsub(model, "eos 600d", "eos rebel t3i")
|
|
||||||
model = string.gsub(model, "eos 650d", "eos rebel t4i")
|
|
||||||
model = string.gsub(model, "eos 700d", "eos rebel t5")
|
|
||||||
model = string.gsub(model, "eos 750d", "eos rebel t6i")
|
|
||||||
model = string.gsub(model, "eos 760d", "eos rebel t6s")
|
|
||||||
model = string.gsub(model, "eos 100d", "eos rebel t6")
|
|
||||||
model = string.gsub(model, "eos 1100d", "eos rebel t3")
|
|
||||||
model = string.gsub(model, "eos 1200d", "eos rebel t5")
|
|
||||||
model = string.gsub(model, "eos 1300d", "eos rebel t6")
|
|
||||||
model = string.gsub(model, "eos 2000d", "eos rebel t7")
|
|
||||||
log.msg(log.debug, "mangle model returning " .. model)
|
|
||||||
end
|
|
||||||
|
|
||||||
restore_log_level(log_level)
|
|
||||||
|
|
||||||
return model
|
|
||||||
end
|
|
||||||
|
|
||||||
local function stop_job()
|
|
||||||
if acs.job then
|
|
||||||
if acs.job.valid then
|
|
||||||
acs.job.valid = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_style_to_images(images)
|
|
||||||
|
|
||||||
local log_level = set_log_level(acs.log_level)
|
|
||||||
|
|
||||||
acs.job = dt.gui.create_job(_("applying camera styles to images"), true, stop_job)
|
|
||||||
|
|
||||||
for count, image in ipairs(images) do
|
|
||||||
local maker = normalize_maker(image.exif_maker)
|
|
||||||
local model = normalize_model(maker, image.exif_model)
|
|
||||||
model = mangle_model(model)
|
|
||||||
log.msg(log.debug, "got maker " .. maker .. " and model " .. model .. " from image " .. image.filename)
|
|
||||||
|
|
||||||
if acs.styles[maker] then
|
|
||||||
local no_match = true
|
|
||||||
for i, pattern in ipairs(acs.styles[maker].patterns) do
|
|
||||||
log.msg(log.debug, "pattern is " .. pattern)
|
|
||||||
if string.match(model, pattern) or
|
|
||||||
(i == #acs.styles[maker].patterns and string.match(pattern, "generic")) then
|
|
||||||
local tag_name = "darktable|style|" .. acs.styles[maker].styles[i].name
|
|
||||||
if not has_style_tag(image, tag_name) then
|
|
||||||
image:apply_style(acs.styles[maker].styles[i])
|
|
||||||
log.msg(log.info, "applied style " .. acs.styles[maker].styles[i].name .. " to " .. image.filename)
|
|
||||||
end
|
|
||||||
no_match = false
|
|
||||||
log.log_level(loglevel)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if no_match then
|
|
||||||
log.msg(log.info, "no style found for " .. maker .. " " .. model)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.msg(log.info, "no maker found for " .. image.filename)
|
|
||||||
end
|
|
||||||
if count % 10 == 0 then
|
|
||||||
acs.job.percent = count / #images
|
|
||||||
end
|
|
||||||
if dt.control.ending then
|
|
||||||
stop_job()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
stop_job()
|
|
||||||
|
|
||||||
restore_log_level(log_level)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_camera_style(collection)
|
|
||||||
|
|
||||||
local log_level = set_log_level(acs.log_level)
|
|
||||||
|
|
||||||
local images = nil
|
|
||||||
|
|
||||||
if collection == true then
|
|
||||||
images = dt.collection
|
|
||||||
log.msg(log.info, "applying camera styles to collection")
|
|
||||||
elseif collection == false then
|
|
||||||
images = dt.gui.selection()
|
|
||||||
if #images == 0 then
|
|
||||||
images = dt.gui.action_images
|
|
||||||
end
|
|
||||||
log.msg(log.info, "applying camera styles to selection")
|
|
||||||
end
|
|
||||||
apply_style_to_images(images)
|
|
||||||
|
|
||||||
restore_log_level(log_level)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
get_camera_styles()
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.destroy_event(MODULE, "post-import-image")
|
|
||||||
dt.destroy_event(MODULE, "post-import-film")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, shortcut)
|
|
||||||
apply_camera_style(true)
|
|
||||||
end, _("apply darktable camera styles to collection")
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, shortcut)
|
|
||||||
apply_camera_style(false)
|
|
||||||
end, _("apply darktable camera styles to selection")
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "post-import-image",
|
|
||||||
function(event, image)
|
|
||||||
if image.is_raw then
|
|
||||||
table.insert(acs.imported_images, image)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "post-import-film",
|
|
||||||
function(event, film)
|
|
||||||
apply_style_to_images(acs.imported_images)
|
|
||||||
acs.imported_images = {}
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
auto_straighten.lua - straighten an image in darkroom using image metadata
|
|
||||||
|
|
||||||
Copyright (C) 2025 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
auto_straighten - straighten an image in darkroom using image metadata
|
|
||||||
|
|
||||||
auto_straighten reads the roll and pitch angle imformation captured when
|
|
||||||
an image is captured. A correction is computed and applied to correct the
|
|
||||||
roll to the nearest vertical or horizontal axis using the rotate and
|
|
||||||
perspective module.
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
exiftool - https://exiftool.org
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
* add it sowewhere in your scripts directory
|
|
||||||
* ensure exiftool is installed and accessible
|
|
||||||
* enable it using script manager
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
Bill Ferguson <wpferguson@gmail.com>
|
|
||||||
|
|
||||||
CHANGES
|
|
||||||
|
|
||||||
LIMITATIONS
|
|
||||||
* Roll and pitch angles are taken when an image is captured. If you shoot
|
|
||||||
in burst mode, the roll and pitch angles of the first image are used in
|
|
||||||
the rest of the burst (at least for Canon).
|
|
||||||
* If you are running multiple scripts that trigger on an image being opened
|
|
||||||
in darkroom then the auto crop may not work. There is a shortcut included
|
|
||||||
that can have a key sequence assigned so that the auto crop will be reapplied.
|
|
||||||
* Currently oply roll angle is corrected
|
|
||||||
|
|
||||||
LIEENSE
|
|
||||||
GPL V2
|
|
||||||
]]
|
|
||||||
|
|
||||||
local dt = require "darktable"
|
|
||||||
local du = require "lib/dtutils"
|
|
||||||
-- local da = require "lib/dtutils.action"
|
|
||||||
local df = require "lib/dtutils.file"
|
|
||||||
-- local ds = require "lib/dtutils.string"
|
|
||||||
-- local dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "auto_straighten"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.info
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("auto_straighten"), -- visible name of script
|
|
||||||
purpose = _("straighten an image in darkroom using image metadata"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/auto_straighten/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local auto_straighten = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
auto_straighten.pixelpipe_busy = false
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.preferences.register(MODULE, "crop_area", "enum",
|
|
||||||
_("automatic_cropping"),
|
|
||||||
_("what automatic cropping to perform"),
|
|
||||||
_("original format"),
|
|
||||||
_("off"), _("largest area"), _("original format"))
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = auto_straighten
|
|
||||||
local as = auto_straighten
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function determine_correction(roll_angle)
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Canon camera roll angle is as foillows
|
|
||||||
* landscape orientation camera upright = 0
|
|
||||||
* camera rolled to the left (shutter button up) portrait = -90
|
|
||||||
* camera inverted landscape = +/- 180
|
|
||||||
* camera rolled to the right (shutter button down) portrait = 90
|
|
||||||
|
|
||||||
rotate and perspective corrections
|
|
||||||
* rotate the image to the right is a negative correction
|
|
||||||
* rotate the image left is a positive correction
|
|
||||||
]]
|
|
||||||
|
|
||||||
if roll_angle == 0 or -- landscape
|
|
||||||
roll_angle == -90 or -- roll left portrait
|
|
||||||
math.abs(roll_angle) == 180 or -- inverted landscape
|
|
||||||
roll_angle == 90 then -- roll right portrait
|
|
||||||
dt.print_log("on axis, no correction required")
|
|
||||||
return 0
|
|
||||||
elseif roll_angle > 0 and roll_angle <= 45 then
|
|
||||||
return (roll_angle * -1.0)
|
|
||||||
elseif roll_angle > 45 and roll_angle < 90 then
|
|
||||||
return (90 - roll_angle)
|
|
||||||
elseif roll_angle > 90 and roll_angle <= 135 then
|
|
||||||
return (roll_angle - 90) * -1.0
|
|
||||||
elseif roll_angle > 135 and roll_angle < 180 then
|
|
||||||
return (180 - roll_angle)
|
|
||||||
elseif roll_angle > -180 and roll_angle <= -135 then
|
|
||||||
return (-180 - roll_angle)
|
|
||||||
elseif roll_angle > -135 and roll_angle <= -90 then
|
|
||||||
return (math.abs(roll_angle) - 90)
|
|
||||||
elseif roll_angle > -90 and roll_angle <= -45 then
|
|
||||||
return (-90 - roll_angle)
|
|
||||||
else
|
|
||||||
return roll_angle * -1.0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function toggle_auto_crop()
|
|
||||||
local crop = dt.preferences.read(MODULE, "crop_area", "enum")
|
|
||||||
dt.gui.action("iop/ashift/automatic cropping", "selection", "item:off", 1.000)
|
|
||||||
dt.control.sleep(100)
|
|
||||||
dt.gui.action("iop/ashift/automatic cropping", "selection", "item:" .. crop, 1.000)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function straighten_image(image)
|
|
||||||
|
|
||||||
local roll = nil
|
|
||||||
local pitch = nil
|
|
||||||
|
|
||||||
-- Roll Angle 0 is landscape, -90 is rolled to the left in portrait, +- 180 is landscape inverted, 90 is portrait rolled to the right
|
|
||||||
|
|
||||||
local crop = dt.preferences.read(MODULE, "crop_area", "enum")
|
|
||||||
|
|
||||||
local pipe = io.popen(as.exiftool .. " -RollAngle -PitchAngle " .. image.path .. PS .. image.filename)
|
|
||||||
if pipe then
|
|
||||||
for line in pipe:lines() do
|
|
||||||
local pr, val = string.match(line, "(.).+: (.+)")
|
|
||||||
if pr == "R" then
|
|
||||||
dt.print_log("roll angle is " .. val)
|
|
||||||
log.msg(log.debug, "roll angle is " .. val)
|
|
||||||
roll = determine_correction(tonumber(val))
|
|
||||||
dt.print_log("corrected roll angle is " .. roll)
|
|
||||||
log.msg(log.debug, "corrected roll angle is " .. roll)
|
|
||||||
elseif pr == "P" then
|
|
||||||
pitch = tonumber(val)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
pipe:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
if roll and dt.gui.action("iop/ashift", "enable", "", "") == 0.0 then
|
|
||||||
dt.print_log("straightening image")
|
|
||||||
dt.gui.action("iop/ashift/rotation", "value", "set", roll)
|
|
||||||
-- dt.gui.action("iop/ashift/automatic cropping", "selection", "item:" .. crop, 1.000)
|
|
||||||
-- da.wait_until_pixelpipe_complete()
|
|
||||||
toggle_auto_crop()
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
as.exiftool = df.check_if_bin_exists("exiftool")
|
|
||||||
|
|
||||||
if not as.exiftool then
|
|
||||||
log.msg(log.error, "exiftool not found, exiting...")
|
|
||||||
log.msg(log.screen, _("exiftool not found, exiting..."))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- da.register_events(MODULE, straighten_image)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.destroy_event(MODULE, "darkroom-image-loaded")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
---[[
|
|
||||||
dt.register_event(MODULE, "darkroom-image-loaded",
|
|
||||||
function(event, clean, image)
|
|
||||||
if clean then
|
|
||||||
straighten_image(image)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
--]]
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, shortcut)
|
|
||||||
toggle_auto_crop()
|
|
||||||
end, "toggle auto crop off and on"
|
|
||||||
)
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -34,23 +34,10 @@ local cjson = require "cjson"
|
|||||||
|
|
||||||
du.check_min_api_version("2.0.0", "check_for_updates")
|
du.check_min_api_version("2.0.0", "check_for_updates")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("check for updates"),
|
|
||||||
purpose = _("check for newer darktable releases"),
|
|
||||||
author = "Tobias Ellinghaus",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/check_for_updates"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
|
|||||||
@@ -27,25 +27,14 @@ USAGE
|
|||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "copy_paste_metadata")
|
du.check_min_api_version("7.0.0", "copy_paste_metadata")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("copy paste metadata"),
|
|
||||||
purpose = _("adds keyboard shortcuts and buttons to copy/paste metadata between images"),
|
|
||||||
author = "Tobias Ellinghaus",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/copy_paste_metadata"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -69,6 +58,13 @@ local publisher = ""
|
|||||||
local rights = ""
|
local rights = ""
|
||||||
local tags = {}
|
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)
|
local function copy(image)
|
||||||
if not image then
|
if not image then
|
||||||
have_data = false
|
have_data = false
|
||||||
@@ -151,13 +147,13 @@ dt.gui.libs.image.register_action(
|
|||||||
dt.register_event(
|
dt.register_event(
|
||||||
"capmd1", "shortcut",
|
"capmd1", "shortcut",
|
||||||
function(event, shortcut) copy(dt.gui.action_images[1]) end,
|
function(event, shortcut) copy(dt.gui.action_images[1]) end,
|
||||||
_("copy metadata")
|
"copy metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
dt.register_event(
|
dt.register_event(
|
||||||
"capmd2", "shortcut",
|
"capmd2", "shortcut",
|
||||||
function(event, shortcut) paste(dt.gui.action_images) end,
|
function(event, shortcut) paste(dt.gui.action_images) end,
|
||||||
_("paste metadata")
|
"paste metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|||||||
@@ -33,23 +33,10 @@ local du = require "lib/dtutils"
|
|||||||
|
|
||||||
du.check_min_api_version("2.0.0", "delete_long_tags")
|
du.check_min_api_version("2.0.0", "delete_long_tags")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("delete long tags"),
|
|
||||||
purpose = _("delete all tags longer than a set length"),
|
|
||||||
author = "Tobias Ellinghaus",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/delete_long_tags"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -72,7 +59,7 @@ local long_tags = {}
|
|||||||
for _,t in ipairs(dt.tags) do
|
for _,t in ipairs(dt.tags) do
|
||||||
local len = #t.name
|
local len = #t.name
|
||||||
if len > max_length then
|
if len > max_length then
|
||||||
dt.print_log("deleting tag `"..t.name.."' (length: "..len..")")
|
print("deleting tag `"..t.name.."' (length: "..len..")")
|
||||||
table.insert(long_tags, t.name)
|
table.insert(long_tags, t.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -37,23 +37,10 @@ local function destroy()
|
|||||||
-- noting to destroy
|
-- noting to destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("delete unused tags"),
|
|
||||||
purpose = _("delete unused tags"),
|
|
||||||
author = "Tobias Ellinghaus",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/delete_unused_tags"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -69,7 +56,7 @@ for _, t in ipairs(dt.tags) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
for _,name in pairs(unused_tags) do
|
for _,name in pairs(unused_tags) do
|
||||||
dt.print_log("deleting tag `" .. name .. "'")
|
print("deleting tag `" .. name .. "'")
|
||||||
tag = dt.tags.find(name)
|
tag = dt.tags.find(name)
|
||||||
tag:delete()
|
tag:delete()
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,40 +39,36 @@ local dtsys = require "lib/dtutils.system"
|
|||||||
|
|
||||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
local gettext = dt.gettext
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "enfuse")
|
du.check_min_api_version("7.0.0", "enfuse")
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("enfuse"),
|
|
||||||
purpose = _("exposure blend images"),
|
|
||||||
author = "Tobias Ellinghaus",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/enfuse"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
||||||
|
|
||||||
|
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||||
|
gettext.bindtextdomain("enfuse",dt.configuration.config_dir..PS .. "lua" .. PS .. "locale" .. PS)
|
||||||
|
|
||||||
local enf = {}
|
local enf = {}
|
||||||
enf.event_registered = false
|
enf.event_registered = false
|
||||||
enf.module_installed = false
|
enf.module_installed = false
|
||||||
enf.lib_widgets = {}
|
enf.lib_widgets = {}
|
||||||
|
|
||||||
|
local function _(msgid)
|
||||||
|
return gettext.dgettext("enfuse", msgid)
|
||||||
|
end
|
||||||
|
|
||||||
local function install_module()
|
local function install_module()
|
||||||
if not enf.module_installed then
|
if not enf.module_installed then
|
||||||
dt.register_lib(
|
dt.register_lib(
|
||||||
"enfuse", -- plugin name
|
"enfuse", -- plugin name
|
||||||
_("enfuse"), -- name
|
"enfuse", -- name
|
||||||
true, -- expandable
|
true, -- expandable
|
||||||
false, -- resetable
|
false, -- resetable
|
||||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||||
@@ -135,8 +131,8 @@ if enfuse_installed then
|
|||||||
if version < "4.2" then
|
if version < "4.2" then
|
||||||
exposure_mu = dt.new_widget("slider")
|
exposure_mu = dt.new_widget("slider")
|
||||||
{
|
{
|
||||||
label = _("exposure mu"),
|
label = "exposure mu",
|
||||||
tooltip = _("center also known as mean of gaussian weighting function (0 <= mean <= 1); default: 0.5"),
|
tooltip = "center also known as MEAN of Gaussian weighting function (0 <= MEAN <= 1); default: 0.5",
|
||||||
hard_min = 0,
|
hard_min = 0,
|
||||||
hard_max = 1,
|
hard_max = 1,
|
||||||
value = dt.preferences.read("enfuse", "exposure_mu", "float")
|
value = dt.preferences.read("enfuse", "exposure_mu", "float")
|
||||||
@@ -144,8 +140,8 @@ if enfuse_installed then
|
|||||||
else
|
else
|
||||||
exposure_mu = dt.new_widget("slider")
|
exposure_mu = dt.new_widget("slider")
|
||||||
{
|
{
|
||||||
label = _("exposure optimum"),
|
label = "exposure optimum",
|
||||||
tooltip = _("optimum exposure value, usually the maximum of the weighting function (0 <= optimum <=1); default 0.5"),
|
tooltip = "optimum exposure value, usually the maximum of the weighting function (0 <= OPTIMUM <=1); default 0.5",
|
||||||
hard_min = 0,
|
hard_min = 0,
|
||||||
hard_max = 1,
|
hard_max = 1,
|
||||||
value = dt.preferences.read("enfuse", "exposure_optimum", "float")
|
value = dt.preferences.read("enfuse", "exposure_optimum", "float")
|
||||||
@@ -154,8 +150,8 @@ if enfuse_installed then
|
|||||||
|
|
||||||
local depth = dt.new_widget("combobox")
|
local depth = dt.new_widget("combobox")
|
||||||
{
|
{
|
||||||
label = _("depth"),
|
label = "depth",
|
||||||
tooltip = _("the number of bits per channel of the output image"),
|
tooltip = "the number of bits per channel of the output image",
|
||||||
value = dt.preferences.read("enfuse", "depth", "integer"),
|
value = dt.preferences.read("enfuse", "depth", "integer"),
|
||||||
changed_callback = function(w) dt.preferences.write("enfuse", "depth", "integer", w.selected) end,
|
changed_callback = function(w) dt.preferences.write("enfuse", "depth", "integer", w.selected) end,
|
||||||
"8", "16", "32"
|
"8", "16", "32"
|
||||||
@@ -163,15 +159,15 @@ if enfuse_installed then
|
|||||||
|
|
||||||
local blend_colorspace = dt.new_widget("combobox")
|
local blend_colorspace = dt.new_widget("combobox")
|
||||||
{
|
{
|
||||||
label = _("blend colorspace"),
|
label = "blend colorspace",
|
||||||
tooltip = _("force blending in selected colorspace"),
|
tooltip = "Force blending in selected colorspace",
|
||||||
changed_callback = function(w) dt.preferences.write("enfuse", "blend_colorspace", "string", w.selected) end,
|
changed_callback = function(w) dt.preferences.write("enfuse", "blend_colorspace", "string", w.selected) end,
|
||||||
"", "identity", "ciecam"
|
"", "identity", "ciecam"
|
||||||
}
|
}
|
||||||
|
|
||||||
local enfuse_button = dt.new_widget("button")
|
local enfuse_button = dt.new_widget("button")
|
||||||
{
|
{
|
||||||
label = enfuse_installed and _("run enfuse") or _("enfuse not installed"),
|
label = enfuse_installed and "run enfuse" or "enfuse not installed",
|
||||||
clicked_callback = function ()
|
clicked_callback = function ()
|
||||||
-- remember exposure_mu
|
-- remember exposure_mu
|
||||||
-- TODO: find a way to save it whenever the value changes
|
-- TODO: find a way to save it whenever the value changes
|
||||||
@@ -189,7 +185,7 @@ if enfuse_installed then
|
|||||||
end
|
end
|
||||||
local f = io.open(response_file, "w")
|
local f = io.open(response_file, "w")
|
||||||
if not f then
|
if not f then
|
||||||
dt.print(string.format(_("error writing to '%s'"), response_file))
|
dt.print(string.format(_("Error writing to `%s`"), response_file))
|
||||||
os.remove(response_file)
|
os.remove(response_file)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -212,9 +208,9 @@ if enfuse_installed then
|
|||||||
if dt.configuration.running_os == "windows" then
|
if dt.configuration.running_os == "windows" then
|
||||||
tmp_exported = dt.configuration.tmp_dir .. tmp_exported -- windows os.tmpname() defaults to root directory
|
tmp_exported = dt.configuration.tmp_dir .. tmp_exported -- windows os.tmpname() defaults to root directory
|
||||||
end
|
end
|
||||||
dt.print(string.format(_("converting raw file '%s' to tiff..."), i.filename))
|
dt.print(string.format(_("Converting raw file '%s' to tiff..."), i.filename))
|
||||||
tiff_exporter:write_image(i, tmp_exported, false)
|
tiff_exporter:write_image(i, tmp_exported, false)
|
||||||
dt.print_log(string.format("raw file '%s' converted to '%s'", i.filename, tmp_exported))
|
dt.print_log(string.format("Raw file '%s' converted to '%s'", i.filename, tmp_exported))
|
||||||
|
|
||||||
cnt = cnt + 1
|
cnt = cnt + 1
|
||||||
f:write(tmp_exported.."\n")
|
f:write(tmp_exported.."\n")
|
||||||
@@ -222,14 +218,14 @@ if enfuse_installed then
|
|||||||
|
|
||||||
-- other images will be skipped
|
-- other images will be skipped
|
||||||
else
|
else
|
||||||
dt.print(string.format(_("skipping %s..."), i.filename))
|
dt.print(string.format(_("Skipping %s..."), i.filename))
|
||||||
n_skipped = n_skipped + 1
|
n_skipped = n_skipped + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
f:close()
|
f:close()
|
||||||
-- bail out if there is nothing to do
|
-- bail out if there is nothing to do
|
||||||
if cnt == 0 then
|
if cnt == 0 then
|
||||||
dt.print(_("no suitable images selected, nothing to do for enfuse"))
|
dt.print(_("No suitable images selected, nothing to do for enfuse"))
|
||||||
os.remove(response_file)
|
os.remove(response_file)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -255,7 +251,7 @@ if enfuse_installed then
|
|||||||
..blend_colorspace_option
|
..blend_colorspace_option
|
||||||
.." -o \""..output_image.."\" \"@"..response_file.."\""
|
.." -o \""..output_image.."\" \"@"..response_file.."\""
|
||||||
if dtsys.external_command( command) > 0 then
|
if dtsys.external_command( command) > 0 then
|
||||||
dt.print(_("enfuse failed, see terminal output for details"))
|
dt.print(_("Enfuse failed, see terminal output for details"))
|
||||||
os.remove(response_file)
|
os.remove(response_file)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -303,7 +299,7 @@ if enfuse_installed then
|
|||||||
else
|
else
|
||||||
dt.print_error("enfuse executable not found")
|
dt.print_error("enfuse executable not found")
|
||||||
error("enfuse executable not found")
|
error("enfuse executable not found")
|
||||||
dt.print(_("could not find enfuse executable, not loading enfuse exporter..."))
|
dt.print(_("Could not find enfuse executable. Not loading enfuse exporter..."))
|
||||||
end
|
end
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|||||||
@@ -1,459 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
extract_burst_roll_images.lua - extract burst images from a burst roll file
|
|
||||||
|
|
||||||
Copyright (C) 2026 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
extract_burst_roll_images - extract burst images from a burst roll file
|
|
||||||
|
|
||||||
Canon cameras are capable of shooting in burst mode. When the shutter
|
|
||||||
button is half pressed the camera starts "recording". When the shutter is
|
|
||||||
fully pressed the pervious 1/2 second of imformation is recorded as individual
|
|
||||||
images stored in one file as well as images captured at 30 frames/sec until
|
|
||||||
the buffer fills or the shutter button is released. So a Canon R7 burst file
|
|
||||||
could contain ~90 images.
|
|
||||||
|
|
||||||
Extracting the images from this file required use of Canon proprietary software
|
|
||||||
until recently when dnglab got the capability to extract the embedded images as
|
|
||||||
DNG raws.
|
|
||||||
|
|
||||||
This script can be set to run on import, scanning for burst roll containers,
|
|
||||||
extracting the embedded images and grouping them with the burst roll container.
|
|
||||||
The script can alsu be utilized via a short cut and applied to selected images.
|
|
||||||
|
|
||||||
When set to on import the script scans each image after import using exiv2 or exiftool
|
|
||||||
to check the image EXIF information to see if the file is a busrt roll container.
|
|
||||||
Once the import images are scanned, the detected burst roll containers are processed,
|
|
||||||
the embedded images extracted as DNSs, and grouped with the container image.
|
|
||||||
|
|
||||||
In shortcut mode the user selects the burst roll containers manually and uses the shortcut
|
|
||||||
to extract the embedded images as DNGs
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
- exiv2 - https://exiv2.org
|
|
||||||
- exiftool - https://exiftool.org
|
|
||||||
- dnglab - https://github.com/dnglab/dnglab
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
- add the script to your lua scripts
|
|
||||||
- enable in script manager
|
|
||||||
- set the preferences in perferences->Lua options
|
|
||||||
|
|
||||||
LIMITATIONS
|
|
||||||
extract_burst_roll_images makes heavy use of external programs to identify burst roll
|
|
||||||
containters and to extract the images. Windows users may want to use it in shortcut mode
|
|
||||||
to lessen the number of windows popping up.
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
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 dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("9.6.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("extract burst roll images"), -- visible name of script
|
|
||||||
purpose = _("extract burst images from a burst roll file"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/extract_burst_roll_images/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "extract_burst_roll_images"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.info
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
local EXIFTOOL <const> = df.check_if_bin_exists("exiftool")
|
|
||||||
local EXIV2 <const> = df.check_if_bin_exists("exiv2")
|
|
||||||
local DNGLAB <const> = df.check_if_bin_exists("dnglab")
|
|
||||||
|
|
||||||
local DNGLAB_ARGS <const> = " convert --image-index all --embed-raw false "
|
|
||||||
local EXIV2_ARGS <const> = " -K Exif.Canon.RawBurstModeRoll -pt "
|
|
||||||
local EXIFTOOL_ARGS <const> = " -T -RawBurstImageCount "
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local extract_burst_roll_images = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
extract_burst_roll_images.preferred_tag_reader = nil
|
|
||||||
extract_burst_roll_images.on_import = true
|
|
||||||
extract_burst_roll_images.imported_images = {}
|
|
||||||
extract_burst_roll_images.log_level = DEFAULT_LOG_LEVEL
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- exiv2 or exiftool
|
|
||||||
dt.preferences.register(MODULE, -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
|
||||||
"tag_reader", -- name
|
|
||||||
"enum", -- type
|
|
||||||
_("preferred exif tag reader"), -- label
|
|
||||||
_("use exiftool or exiv2 to read exif tags"), -- tooltip
|
|
||||||
"exiftool", -- default
|
|
||||||
"exiftool", "exiv2") -- values
|
|
||||||
|
|
||||||
-- run on import
|
|
||||||
dt.preferences.register(MODULE, -- script: This is a string used to avoid name collision in preferences (i.e namespace). Set it to something unique, usually the name of the script handling the preference.
|
|
||||||
"on_import", -- name
|
|
||||||
"bool", -- type
|
|
||||||
_("extract burst roll images on import"), -- label
|
|
||||||
_("extract burst roll images on import"), -- tooltip
|
|
||||||
true) -- default
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = extract_burst_roll_images
|
|
||||||
local ebri = extract_burst_roll_images
|
|
||||||
|
|
||||||
local sleep = dt.control.sleep
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
-- helper functions
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
local function set_log_level(level)
|
|
||||||
if not level then
|
|
||||||
level = namespace.log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
local old_log_level = log.log_level()
|
|
||||||
log.log_level(level)
|
|
||||||
|
|
||||||
return old_log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_log_level(level)
|
|
||||||
if not level then
|
|
||||||
level = namespace.log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
log.log_level(level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function reset_log_level()
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
namespace.log_level = DEFAULT_LOG_LEVEL
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pref_read(name, pref_type)
|
|
||||||
local old_log_level = set_log_level(ebri.log_level)
|
|
||||||
|
|
||||||
log.msg(log.debug, "name is " .. name .. " and type is " .. pref_type)
|
|
||||||
|
|
||||||
local val = dt.preferences.read(MODULE, name, pref_type)
|
|
||||||
|
|
||||||
log.msg(log.debug, "read value " .. tostring(val))
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pref_write(name, pref_type, value)
|
|
||||||
local old_log_level = set_log_level(ebri.log_level)
|
|
||||||
|
|
||||||
log.msg(log.debug, "writing value " .. tostring(value) .. " for name " .. name)
|
|
||||||
|
|
||||||
dt.preferences.write(MODULE, name, pref_type, value)
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_last_value(valuestr)
|
|
||||||
local old_log_level = set_log_level()
|
|
||||||
local parts = du.split(valuestr, " +")
|
|
||||||
|
|
||||||
if parts then
|
|
||||||
log.msg(log.debug, "last value of parts is " .. tostring(parts[#parts]))
|
|
||||||
return parts[#parts]
|
|
||||||
else
|
|
||||||
log.msg(log.debug, "last part not detected, returning nil")
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_burst_roll_image(image)
|
|
||||||
local old_log_level = set_log_level()
|
|
||||||
local result = false
|
|
||||||
local cmd = EXIFTOOL .. EXIFTOOL_ARGS
|
|
||||||
|
|
||||||
if ebri.preferred_tag_reader == "exiv2" then
|
|
||||||
cmd = EXIV2 .. EXIV2_ARGS
|
|
||||||
end
|
|
||||||
|
|
||||||
local p = io.popen(cmd .. ds.sanitize(image.path) .. PS .. ds.sanitize(image.filename))
|
|
||||||
if p then
|
|
||||||
for line in p:lines() do
|
|
||||||
if ebri.preferred_tag_reader == "exiv2" then
|
|
||||||
line = get_last_value(line)
|
|
||||||
end
|
|
||||||
line = tonumber(line)
|
|
||||||
if type(line) == "number" then
|
|
||||||
if tonumber(line) > 0 then
|
|
||||||
log.msg(log.info, image.filename .. " is a burst roll container image")
|
|
||||||
result = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
p:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
local function group_extracted_dngs(image)
|
|
||||||
local old_log_level = set_log_level()
|
|
||||||
local count = 0
|
|
||||||
|
|
||||||
local cmd = "ls -1 " .. ds.sanitize(image.path) .. PS .. ds.sanitize(ds.get_basename(image.filename)) .. "*.dng"
|
|
||||||
|
|
||||||
if dt.configuration.running_os == "windows" then
|
|
||||||
cmd = "forfiles /P " .. ds.sanitize(image.path) .. " /M " .. ds.sanitize(ds.get_basename(image.filename)) .. "*.dng" .. " /C \"cmd /c echo @path\\@fname\""
|
|
||||||
end
|
|
||||||
|
|
||||||
local p = io.popen(cmd)
|
|
||||||
if p then
|
|
||||||
for line in p:lines() do
|
|
||||||
if line:length() > 5 then -- skip blank lines created by forfiles
|
|
||||||
local dng_image = dt.database.import(line)
|
|
||||||
dng_image:group_with(image)
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
p:close()
|
|
||||||
log.msg(log.screen, string.format(_("extracted %d dng files from %s"), count, image.filename))
|
|
||||||
end
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function extract_burst(image)
|
|
||||||
local old_log_level = set_log_level()
|
|
||||||
|
|
||||||
local cmd = DNGLAB .. DNGLAB_ARGS .. ds.sanitize(image.path) .. PS .. ds.sanitize(image.filename) .. " " .. ds.sanitize(image.path)
|
|
||||||
|
|
||||||
local result = dtsys.external_command(cmd)
|
|
||||||
|
|
||||||
if result == 0 then
|
|
||||||
group_extracted_dngs(image)
|
|
||||||
end
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function process_image(image)
|
|
||||||
local old_log_level = set_log_level()
|
|
||||||
if is_burst_roll_image(image) then
|
|
||||||
extract_burst(image)
|
|
||||||
end
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function stop_job(job)
|
|
||||||
job.valid = false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function process_images(images)
|
|
||||||
local old_log_level = set_log_level()
|
|
||||||
local job = dt.gui.create_job(_("scanning for burst roll images"), true, stop_job)
|
|
||||||
|
|
||||||
for i, image in ipairs(images) do
|
|
||||||
if ebri.job.valid then
|
|
||||||
extract_burst(image)
|
|
||||||
ebri.job.percent = i / #images
|
|
||||||
sleep(10)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
stop_job(job)
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function process_import(images)
|
|
||||||
local old_log_level = set_log_level()
|
|
||||||
local burst_roll_images = {}
|
|
||||||
local job = dt.gui.create_job(_("scanning for burst roll images"), true, stop_job)
|
|
||||||
|
|
||||||
for i, image in ipairs(images) do
|
|
||||||
if job.valid then
|
|
||||||
if is_burst_roll_image(image) then
|
|
||||||
table.insert(burst_roll_images, image)
|
|
||||||
end
|
|
||||||
if i % 10 == 0 then
|
|
||||||
job.percent = i / #images
|
|
||||||
end
|
|
||||||
sleep(10)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
stop_job(job)
|
|
||||||
|
|
||||||
if #burst_roll_images > 0 then
|
|
||||||
job = dt.gui.create_job(_("extracting burst roll images"), true, stop_job)
|
|
||||||
for j, image in ipairs(burst_roll_images) do
|
|
||||||
if job.valid then
|
|
||||||
extract_burst(image)
|
|
||||||
job.percent = j / #burst_roll_images
|
|
||||||
sleep(10)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
stop_job(job)
|
|
||||||
end
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
if not DNGLAB then
|
|
||||||
log.msg(log.error, MODULE .. _("dnglab executable not found"))
|
|
||||||
log.msg(log.screen, script_data.metadata.name .. _(": dnglab executable not found, exiting..."))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not EXIFTOOL and not EXIV2 then
|
|
||||||
log.msg(log.error, MODULE .. _("no exif tag reader available"))
|
|
||||||
log.msg(log.screen, script_data.metadata.name .. _(" no exif tag reader available, exiting..."))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
ebri.preferred_tag_reader = pref_read("tag_reader", "enum")
|
|
||||||
|
|
||||||
if not string.match(EXIFTOOL, ebri.preferred_tag_reader) and not string.match(EXIV2, ebri.preferred_tag_reader) then
|
|
||||||
log.msg(log.error, MODULE .. " selected tag " .. ebri.preferred_tag_reader .." not available")
|
|
||||||
log.msg(log.screen, script_data.metadata.name .. string.format(_(" selected tag reader %s not available"), ebri.preferred_tag_reader))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
ebri.on_import = dt.preferences.read(MODULE, "on_import", "bool")
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.destroy_event(MODULE, "post-import-film")
|
|
||||||
dt.destroy_event(MODULE, "post-import-image")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "post-import-film",
|
|
||||||
function(event, film)
|
|
||||||
process_import(ebri.imported_images)
|
|
||||||
ebri.imported_images = {}
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "post-import-image",
|
|
||||||
function(event, image)
|
|
||||||
if ebri.on_import then
|
|
||||||
table.insert(ebri.imported_images, image)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
if not dt.query_event(MODULE, "shortcut") then
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, shortcut)
|
|
||||||
if #dt.gui.action_images > 1 then
|
|
||||||
process_images(dt.gui.action_images)
|
|
||||||
else
|
|
||||||
process_image(dt.gui.action_images[1])
|
|
||||||
end
|
|
||||||
end, "extract roll burst images"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -38,25 +38,12 @@ local dt = require "darktable"
|
|||||||
local du = require "lib/dtutils"
|
local du = require "lib/dtutils"
|
||||||
require "darktable.debug"
|
require "darktable.debug"
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", "generate_image_txt")
|
du.check_min_api_version("7.0.0", "generate_image_txt")
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("generate image text"),
|
|
||||||
purpose = _("overlay metadata on the selected image(s)"),
|
|
||||||
author = "Tobias Ellinghaus",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/generate_image_txt"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -65,21 +52,21 @@ script_data.show = nil -- only required for libs since the destroy_method only h
|
|||||||
dt.preferences.register("generate_image_txt",
|
dt.preferences.register("generate_image_txt",
|
||||||
"enabled",
|
"enabled",
|
||||||
"bool",
|
"bool",
|
||||||
_("create txt sidecars to display with images"),
|
"create txt sidecars to display with images",
|
||||||
_("the txt files created get shown when the lighttable is zoomed in to one image. also enable the txt overlay setting in the gui tab"),
|
"the txt files created get shown when the lighttable is zoomed in to one image. also enable the txt overlay setting in the gui tab",
|
||||||
false)
|
false)
|
||||||
|
|
||||||
dt.preferences.register("generate_image_txt",
|
dt.preferences.register("generate_image_txt",
|
||||||
"command",
|
"command",
|
||||||
"string",
|
"string",
|
||||||
_("command to generate the txt sidecar"),
|
"command to generate the txt sidecar",
|
||||||
_("the output of this command gets written to the txt file. use $(FILE_NAME) for the image file"),
|
"the output of this command gets written to the txt file. use $(FILE_NAME) for the image file",
|
||||||
"exiv2 $(FILE_NAME)")
|
"exiv2 $(FILE_NAME)")
|
||||||
|
|
||||||
|
|
||||||
local check_command = function(command)
|
local check_command = function(command)
|
||||||
if not command:find("$(FILE_NAME)", 1, true) then
|
if not command:find("$(FILE_NAME)", 1, true) then
|
||||||
dt.print(_("the command for txt sidecars looks bad. better check the preferences"))
|
dt.print("the command for txt sidecars looks bad. better check the preferences")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,694 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
group_persistence.lua - preserve groups between darktable instances
|
|
||||||
|
|
||||||
Copyright (C) 2024-2026 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
group_persistence - preserve groups between darktable instances
|
|
||||||
|
|
||||||
group_persistence uses "functional" tagging to maintain image groups
|
|
||||||
across database instances, allowing image grouping to be restored when
|
|
||||||
the database is rebuilt from the XMP sidecar files.
|
|
||||||
|
|
||||||
Once the script is started it "listens" for changes in image grouping and
|
|
||||||
applies or removes the necessary tags automatically.
|
|
||||||
|
|
||||||
A shortcut can be assigned to read a collection and apply the necessary
|
|
||||||
grouping tags so that existing collections can be maintained.
|
|
||||||
|
|
||||||
If the database is lost and needs to be rebuilt then start the script before
|
|
||||||
importing any images. As images are imported the tags will be read and the
|
|
||||||
grouping applied in the database.
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
|
|
||||||
* Enable the script using script manager
|
|
||||||
* Optionally, create a keystroke combination for the shortcut
|
|
||||||
|
|
||||||
LIMITATIONS
|
|
||||||
|
|
||||||
In normal use you wont notice that the script is running.
|
|
||||||
|
|
||||||
However if you do something like import 4000+ images, then
|
|
||||||
run the autogrouper script to create groups whenever there is
|
|
||||||
a 5 second difference between 2 images, your system will be busy
|
|
||||||
for awhile. If for some reason you need to do something like this
|
|
||||||
turn off group_persistence, run the autogrouper, turn on group_persistence
|
|
||||||
and use the shortcut to read all the grouping information and apply
|
|
||||||
the necessary tags. It will still be an impact, but less than trying
|
|
||||||
to do both at once. Learned from experience :-)
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
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 dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "group_persistence"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.error
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("9.4.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
dt.gettext.bindtextdomain(MODULE , dt.configuration.config_dir .. "/lua/locale/")
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = "group_persistence", -- name of script
|
|
||||||
purpose = _("preserve groups between darktable instances"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local group_persistence = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = group_persistence
|
|
||||||
|
|
||||||
local gp = group_persistence
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- table of imported images that need grouping rebuilt
|
|
||||||
gp.imported_images = {}
|
|
||||||
|
|
||||||
-- uuid to group leader mapping
|
|
||||||
gp.uuids = {}
|
|
||||||
|
|
||||||
-- group leader to uuid mapping
|
|
||||||
gp.leaders = {}
|
|
||||||
|
|
||||||
-- uuid to group tag mapping
|
|
||||||
gp.group_tags = {}
|
|
||||||
|
|
||||||
-- uuid to group leader tag mapping
|
|
||||||
gp.leader_tags = {}
|
|
||||||
|
|
||||||
-- when the groups are rebuilt after import they
|
|
||||||
-- cause the group change events to fire but the
|
|
||||||
-- events are queued until after rebuilding completes
|
|
||||||
-- so we need to build a table of images to ignore when
|
|
||||||
-- the queued events catch up. As the event fires for an
|
|
||||||
-- image, the image is removed.
|
|
||||||
gp.rebuilt_images = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- utilities for building/debugging
|
|
||||||
|
|
||||||
local function dump_image_tags(image)
|
|
||||||
log.msg(log.debug, "")
|
|
||||||
|
|
||||||
for _, tag in ipairs(image:get_tags()) do
|
|
||||||
log.msg(log.debug, "tag for image " .. image.id .. " is " .. tag.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
log.msg(log.debug, "group_leader is " .. image.group_leader.id .. " and group has " .. #image:get_group_members() .. " members")
|
|
||||||
|
|
||||||
log.msg(log.debug, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dump_group_tags(image)
|
|
||||||
local leader = image.group_leader
|
|
||||||
|
|
||||||
for _, tag in ipairs(image:get_tags()) do
|
|
||||||
if string.match(tag.name, "darktable|group") then
|
|
||||||
log.msg(log.debug, "group leader image " .. image.id .. " has tag " .. tag.name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, img in ipairs(image:get_group_members()) do
|
|
||||||
for _, tag in ipairs(img:get_tags()) do
|
|
||||||
if string.match(tag.name, "darktable|group") then
|
|
||||||
log.msg(log.debug, "group member image " .. img.id .. " has tag " .. tag.name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- rebuild grouping on import
|
|
||||||
|
|
||||||
local function save_group_leader(image, uuid)
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
log.msg(log.debug, "input image is " .. image.id .. " and uuid is " .. uuid)
|
|
||||||
|
|
||||||
gp.uuids[image] = uuid
|
|
||||||
gp.leaders[uuid] = image
|
|
||||||
|
|
||||||
log.msg(log.debug, "after saving group leader uuid is " .. gp.leaders[uuid].id .. " and image is " .. gp.uuids[image])
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_group_and_uuid(tags)
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
local group_type = nil
|
|
||||||
local uuid = nil
|
|
||||||
|
|
||||||
for _, tag in ipairs(tags) do
|
|
||||||
|
|
||||||
if string.match(tag.name, "darktable|group") then
|
|
||||||
|
|
||||||
log.msg(log.debug, "found tag darktable|group")
|
|
||||||
|
|
||||||
local parts = du.split(tag.name, "|")
|
|
||||||
|
|
||||||
group_type = parts[2]
|
|
||||||
uuid = parts[3]
|
|
||||||
|
|
||||||
log.msg(log.info, "found type " .. group_type .. " with uuid " .. uuid)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return group_type, uuid
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_uuid_from_tag(tag)
|
|
||||||
local parts = du.split(tag.name, "|")
|
|
||||||
return parts[#parts]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_group_leader(uuid)
|
|
||||||
|
|
||||||
local group_leader = nil
|
|
||||||
|
|
||||||
local tag_name = "darktable|group_leader|" .. uuid
|
|
||||||
|
|
||||||
local found_tag = dt.tags.find(tag_name)
|
|
||||||
|
|
||||||
if found_tag then
|
|
||||||
|
|
||||||
group_leader = dt.tags.get_tagged_images(found_tag)[1]
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
return group_leader
|
|
||||||
end
|
|
||||||
|
|
||||||
local function rebuild_groups()
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
for _, image in ipairs(gp.imported_images) do
|
|
||||||
|
|
||||||
log.msg(log.info, "processing image " .. image.id)
|
|
||||||
|
|
||||||
local tags = image:get_tags()
|
|
||||||
|
|
||||||
local group_type, uuid = get_group_and_uuid(tags)
|
|
||||||
|
|
||||||
if group_type =="group_leader" then
|
|
||||||
|
|
||||||
table.insert(gp.rebuilt_images, image)
|
|
||||||
|
|
||||||
log.msg(log.debug, "took group_leader branch")
|
|
||||||
|
|
||||||
save_group_leader(image, uuid)
|
|
||||||
|
|
||||||
elseif group_type == "group" then
|
|
||||||
|
|
||||||
table.insert(gp.rebuilt_images, image)
|
|
||||||
|
|
||||||
log.msg(log.debug, "took group branch")
|
|
||||||
|
|
||||||
local leader = gp.uuids[uuid]
|
|
||||||
|
|
||||||
if leader then
|
|
||||||
|
|
||||||
image:group_with(leader)
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
local group_leader = find_group_leader(uuid)
|
|
||||||
|
|
||||||
if group_leader then
|
|
||||||
|
|
||||||
save_group_leader(group_leader, uuid)
|
|
||||||
image:group_with(group_leader)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
gp.imported_images = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- rebuild grouping on command (shortcut)
|
|
||||||
|
|
||||||
local function is_group_leader(image)
|
|
||||||
result = false
|
|
||||||
|
|
||||||
if image.group_leader == image and #image:get_group_members() > 1 then
|
|
||||||
result = true
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
local function has_group_tag(image)
|
|
||||||
local result = false
|
|
||||||
|
|
||||||
for _, tag in ipairs(image:get_tags()) do
|
|
||||||
if string.match(tag.name, "darktable|group|") then
|
|
||||||
result = tag
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
local function has_group_leader_tag(image)
|
|
||||||
local result = false
|
|
||||||
|
|
||||||
for _, tag in ipairs(image:get_tags()) do
|
|
||||||
if string.match(tag.name, "darktable|group_leader") then
|
|
||||||
result = tag
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle grouping changes in the UI
|
|
||||||
|
|
||||||
local function group_exists(group_leader)
|
|
||||||
local exists = false
|
|
||||||
|
|
||||||
local tags = group_leader:get_tags()
|
|
||||||
|
|
||||||
for _, tag in ipairs(tags) do
|
|
||||||
if string.match(tag.name, "group_leader") then
|
|
||||||
exists = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return exists
|
|
||||||
end
|
|
||||||
|
|
||||||
local function add_group_leader(image, group)
|
|
||||||
-- tag image as group leader
|
|
||||||
-- create the uuid and leader/group tags if necessary
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
local uuid = du.gen_uuid()
|
|
||||||
|
|
||||||
if uuid then
|
|
||||||
log.msg(log.debug, "in add_group_leader and uuid is " .. uuid)
|
|
||||||
else
|
|
||||||
log.msg(log.debug, "uuid not created")
|
|
||||||
end
|
|
||||||
|
|
||||||
local tag = dt.tags.create("darktable|group_leader|" .. uuid)
|
|
||||||
|
|
||||||
local group_tag = dt.tags.create("darktable|group|" .. uuid)
|
|
||||||
|
|
||||||
log.msg(log.info, "created group leader tag " .. tag.name)
|
|
||||||
log.msg(log.info, "created group tag " .. group_tag.name)
|
|
||||||
|
|
||||||
gp.group_tags[uuid] = group_tag
|
|
||||||
gp.leader_tags[uuid] = tag
|
|
||||||
|
|
||||||
gp.uuids[uuid] = image
|
|
||||||
gp.leaders[image] = uuid
|
|
||||||
|
|
||||||
image:attach_tag(tag)
|
|
||||||
log.msg(log.info, "attached tag " .. tag.name .. " to image " .. image.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function add_to_group(image, group_leader)
|
|
||||||
-- add an image to a group
|
|
||||||
-- create the group if necessary
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
if image == group_leader then
|
|
||||||
|
|
||||||
add_group_leader(image, group_leader)
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
if not group_exists(group_leader) then
|
|
||||||
add_group_leader(group_leader, group_leader)
|
|
||||||
end
|
|
||||||
|
|
||||||
local group_tag = nil
|
|
||||||
|
|
||||||
log.msg(log.debug, "image is " .. image.id .. " and group_leader is " .. image.group_leader.id)
|
|
||||||
|
|
||||||
local group, uuid = get_group_and_uuid(image.group_leader:get_tags())
|
|
||||||
|
|
||||||
group_tag = gp.group_tags[uuid]
|
|
||||||
|
|
||||||
if not group_tag then
|
|
||||||
log.msg(log.info, "creating group tag darktable|group|" .. uuid)
|
|
||||||
group_tag = dt.tags.create("darktable|group|" .. uuid)
|
|
||||||
gp.group_tags[uuid] = group_tag
|
|
||||||
end
|
|
||||||
|
|
||||||
image:attach_tag(group_tag)
|
|
||||||
log.msg(log.info, "group tag attached to image " .. image.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function build_group(leader)
|
|
||||||
|
|
||||||
if not has_group_leader_tag(leader) then
|
|
||||||
add_group_leader(leader)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, image in ipairs(leader:get_group_members()) do
|
|
||||||
if not has_group_tag(image) then
|
|
||||||
add_to_group(image, leader)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function process(image_set)
|
|
||||||
local images
|
|
||||||
|
|
||||||
if image_set == "collection" then
|
|
||||||
images = dt.collection
|
|
||||||
else
|
|
||||||
images = dt.gui.selection()
|
|
||||||
if #images == 0 then
|
|
||||||
images = dt.gui.action_images
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, image in ipairs(images) do
|
|
||||||
if is_group_leader(image) then
|
|
||||||
build_group(image)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function check_and_remove_empty_group_data(group_tag)
|
|
||||||
if not group_tag then
|
|
||||||
return -- handle duplicate getting deleted
|
|
||||||
end
|
|
||||||
|
|
||||||
local group_leader_tag = nil
|
|
||||||
|
|
||||||
group_leader_tag = dt.tags.find(string.gsub(group_tag.name, "|group|", "|group_leader|"))
|
|
||||||
|
|
||||||
local group_leader_count = 0
|
|
||||||
local group_count = #dt.tags.get_tagged_images(group_tag)
|
|
||||||
|
|
||||||
if group_leader_tag then
|
|
||||||
group_leader_count = #dt.tags.get_tagged_images(group_leader_tag)
|
|
||||||
end
|
|
||||||
|
|
||||||
if group_leader_count + group_count < 2 then -- there's either 1 or 0, so no group
|
|
||||||
local uuid = get_uuid_from_tag(group_tag)
|
|
||||||
|
|
||||||
if gp.group_tags[uuid] then
|
|
||||||
gp.group_tags[uuid] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if gp.uuids[image] then
|
|
||||||
gp.uuids[image] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if gp.leaders[uuid] then
|
|
||||||
gp.leaders[uuid] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
group_tag:delete()
|
|
||||||
if group_leader_tag then
|
|
||||||
group_leader_tag:delete()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function remove_group_leader(image, new_group_leader)
|
|
||||||
-- remove image group leader tag
|
|
||||||
-- if it's the last image, then remove the leader/group tags
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
dump_image_tags(image)
|
|
||||||
|
|
||||||
local group, uuid = get_group_and_uuid(image:get_tags())
|
|
||||||
|
|
||||||
local leader_tag = dt.tags.find("darktable|group_leader|" .. uuid)
|
|
||||||
local group_tag = dt.tags.find("darktable|group|" .. uuid)
|
|
||||||
|
|
||||||
image:detach_tag(leader_tag)
|
|
||||||
gp.leaders[image] = nil
|
|
||||||
|
|
||||||
log.msg(log.info, "removed image " .. image.id .. " as group leader for uuid " .. uuid)
|
|
||||||
|
|
||||||
new_group_leader:detach_tag(group_tag)
|
|
||||||
new_group_leader:attach_tag(leader_tag)
|
|
||||||
|
|
||||||
log.msg(log.info, "added image " .. new_group_leader.id .. " as group leader for uuid " .. uuid)
|
|
||||||
|
|
||||||
gp.leaders[new_group_leader] = uuid
|
|
||||||
gp.uuids[uuid] = new_group_leader
|
|
||||||
|
|
||||||
check_and_remove_empty_group_data(group_tag)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function remove_from_group(image, group_leader)
|
|
||||||
-- remove an image from a group
|
|
||||||
-- if it's the last image then remove the tags
|
|
||||||
|
|
||||||
-- image.id > group_leder.id - simple remove
|
|
||||||
-- image.id < group_leader.id - deleting old group_leader (image) with new group_leader (group_leader)
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
if image.id < group_leader.id then
|
|
||||||
|
|
||||||
remove_group_leader(image, group_leader)
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
local group_tag = has_group_tag(image)
|
|
||||||
|
|
||||||
if group_tag then
|
|
||||||
|
|
||||||
image:detach_tag(group_tag)
|
|
||||||
|
|
||||||
log.msg(log.info, "image " .. image.filename .. " with id " .. image.id .. " removed from group " .. group_tag.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
check_and_remove_empty_group_data(group_tag)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function change_group_leader(image)
|
|
||||||
-- remove the group leader tag from the old leader
|
|
||||||
-- replace the old group leader tag with a group tag
|
|
||||||
-- add the group leader tag to the new image
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
dump_image_tags(image)
|
|
||||||
|
|
||||||
local group, uuid = get_group_and_uuid(image:get_tags())
|
|
||||||
|
|
||||||
local leader_tag = dt.tags.find("darktable|group_leader|" .. uuid)
|
|
||||||
local group_tag = dt.tags.find("darktable|group|" .. uuid)
|
|
||||||
|
|
||||||
old_leader = dt.tags.get_tagged_images(leader_tag)[1]
|
|
||||||
|
|
||||||
old_leader:detach_tag(leader_tag)
|
|
||||||
log.msg(log.info, "remove leader tag from image " .. old_leader.id)
|
|
||||||
old_leader:attach_tag(group_tag)
|
|
||||||
log.msg(log.info, "attached group tag to image " .. old_leader.id)
|
|
||||||
image:detach_tag(group_tag)
|
|
||||||
log.msg(log.info, "removed group tag from new leader image " .. image.id)
|
|
||||||
image:attach_tag(leader_tag)
|
|
||||||
log.msg(log.info, "attached leader tag to new leader image " .. image.id)
|
|
||||||
gp.leaders[uuid] = image
|
|
||||||
gp.uuids[image] = uuid
|
|
||||||
end
|
|
||||||
|
|
||||||
-- event handling
|
|
||||||
|
|
||||||
local function add_group_event()
|
|
||||||
dt.register_event(MODULE, "image-group-information-changed",
|
|
||||||
function(event, reason, image, other_image)
|
|
||||||
log.msg(log.debug, "")
|
|
||||||
log.msg(log.debug, "caught event with reason " .. reason .. " and image " .. image.id .. " and other image " .. other_image.id)
|
|
||||||
log.msg(log.debug, "")
|
|
||||||
for pos, rimage in ipairs(gp.rebuilt_images) do
|
|
||||||
if rimage == image then
|
|
||||||
table.remove(gp.rebuilt_images, pos)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if reason == "add" then
|
|
||||||
log.msg(log.debug, "in add, image is " .. image.id .. " and group is " .. other_image.id)
|
|
||||||
if image == other_image then
|
|
||||||
add_group_leader(image, other_image)
|
|
||||||
else
|
|
||||||
add_to_group(image, other_image)
|
|
||||||
end
|
|
||||||
elseif reason == "remove" then
|
|
||||||
log.msg(log.debug, "in remove with image " .. image.id .. " and group_id " .. other_image.id)
|
|
||||||
remove_from_group(image, other_image)
|
|
||||||
elseif reason == "remove-leader" then
|
|
||||||
remove_group_leader(image, other_image)
|
|
||||||
log.msg(log.debug, "in remove-leader with image " .. image.id .. " and new group id " .. other_image.id)
|
|
||||||
else
|
|
||||||
if image ~= other_image then
|
|
||||||
log.msg(log.debug, "in change-leader with image " .. image.id .. " and other_id " .. other_image.id)
|
|
||||||
change_group_leader(image)
|
|
||||||
log.msg(log.debug, "in change-leader with image " .. image.id .. " and other_id " .. other_image.id)
|
|
||||||
else
|
|
||||||
log.msg(log.debug, "in change-leader with image " .. image.id .. " and other_id " .. other_image.id)
|
|
||||||
log.msg(log.debug, "calling add_group_leader")
|
|
||||||
add_group_leader(image, other_image)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.util.message(MODULE, "autogrouper", "running")
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.destroy_event(MODULE, "post-import-image")
|
|
||||||
dt.destroy_event(MODULE, "post-import-film")
|
|
||||||
dt.destroy_event(MODULE, "image-group-information-changed")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "post-import-image",
|
|
||||||
function(event, image)
|
|
||||||
table.insert(gp.imported_images, image)
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "post-import-film",
|
|
||||||
function(event, filmroll)
|
|
||||||
rebuild_groups()
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
add_group_event()
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, string)
|
|
||||||
dump_image_tags(dt.gui.action_images[1])
|
|
||||||
end, "dump image tags"
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, string)
|
|
||||||
dump_group_tags(dt.gui.action_images[1])
|
|
||||||
end, "dump group_tags"
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, string)
|
|
||||||
process("collection")
|
|
||||||
end, "group collection"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -33,23 +33,10 @@ local du = require "lib/dtutils"
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", "image_path_in_ui")
|
du.check_min_api_version("7.0.0", "image_path_in_ui")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("image path in UI"),
|
|
||||||
purpose = _("print the image path in the UI"),
|
|
||||||
author = "Jérémy Rosen",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/image_path_in_ui"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -63,7 +50,7 @@ local main_label = dt.new_widget("label"){selectable = true, ellipsize = "middle
|
|||||||
|
|
||||||
local function install_module()
|
local function install_module()
|
||||||
if not ipiu.module_installed then
|
if not ipiu.module_installed then
|
||||||
dt.register_lib("image_path_no_ui",_("selected images path"),true,false,{
|
dt.register_lib("image_path_no_ui","selected images path",true,false,{
|
||||||
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER",300}
|
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER",300}
|
||||||
}, main_label
|
}, main_label
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,40 +32,20 @@ USAGE
|
|||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("import filter manager"),
|
|
||||||
purpose = _("manage import filters"),
|
|
||||||
author = "Tobias Ellinghaus",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/import_filter_manager"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
local import_filter_list = {}
|
local import_filter_list = {}
|
||||||
local n_import_filters = 1
|
local n_import_filters = 1
|
||||||
|
|
||||||
-- allow changing the filter from the preferences
|
-- allow changing the filter from the preferences
|
||||||
dt.preferences.register("import_filter_manager", "active_filter", "string",
|
dt.preferences.register("import_filter_manager", "active_filter", "string",
|
||||||
_("import filter"), _("the name of the filter used for importing images"), "")
|
"import filter", "the name of the filter used for importing images", "")
|
||||||
|
|
||||||
|
|
||||||
-- the dropdown to select the active filter from the import dialog
|
-- the dropdown to select the active filter from the import dialog
|
||||||
local filter_dropdown = dt.new_widget("combobox")
|
local filter_dropdown = dt.new_widget("combobox")
|
||||||
{
|
{
|
||||||
label = _("import filter"),
|
label = "import filter",
|
||||||
editable = false,
|
editable = false,
|
||||||
tooltip = _("import filters are applied after completion of the import dialog"),
|
tooltip = "import filters are applied after completion of the import dialog",
|
||||||
|
|
||||||
changed_callback = function(widget)
|
changed_callback = function(widget)
|
||||||
dt.preferences.write("import_filter_manager", "active_filter", "string", widget.value)
|
dt.preferences.write("import_filter_manager", "active_filter", "string", widget.value)
|
||||||
@@ -95,12 +75,6 @@ dt.register_import_filter = function(name, callback)
|
|||||||
if name == active_filter then filter_dropdown.value = n_import_filters end
|
if name == active_filter then filter_dropdown.value = n_import_filters end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
--noting to destroy
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
|
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
|
||||||
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;
|
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;
|
||||||
|
|||||||
@@ -30,31 +30,11 @@ USAGE
|
|||||||
|
|
||||||
local dt = require "darktable"
|
local dt = require "darktable"
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("import filters"),
|
|
||||||
purpose = _("import filtering"),
|
|
||||||
author = "Tobias Ellinghaus & Christian Mandel",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/import_filters"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
-- we get fed a sorted list of filenames. just setting images to ignore to nil is enough
|
-- we get fed a sorted list of filenames. just setting images to ignore to nil is enough
|
||||||
|
|
||||||
-- ignore jpeg
|
-- ignore jpeg
|
||||||
dt.register_import_filter("ignore jpegs", function(event, images)
|
dt.register_import_filter("ignore jpegs", function(event, images)
|
||||||
dt.print_log("ignoring all jpegs")
|
dt.print_error("ignoring all jpegs")
|
||||||
for i, img in ipairs(images) do
|
for i, img in ipairs(images) do
|
||||||
local extension = img:match("[^.]*$"):upper()
|
local extension = img:match("[^.]*$"):upper()
|
||||||
if (extension == "JPG") or (extension == "JPEG") then
|
if (extension == "JPG") or (extension == "JPEG") then
|
||||||
@@ -108,13 +88,5 @@ dt.register_import_filter("prefer raw over jpeg", function(event, images)
|
|||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
-- nothing to destroy
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
|
|
||||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
|
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
|
||||||
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;
|
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;
|
||||||
|
|||||||
@@ -1,343 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
recent_bookmarks.lua - add recently edited and exported images to system recent bookmarks
|
|
||||||
|
|
||||||
Copyright (C) 2026 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
recent_bookmarks - add recently edited and exported images to system recently used files
|
|
||||||
|
|
||||||
recent_bookmarks adds images edited in darktable and the directory of images exported from
|
|
||||||
darktable to the reccently-used.xbel file so that they show up in the recently used system
|
|
||||||
shortcuts.
|
|
||||||
|
|
||||||
Currently recent_bookmarks only works on Linux
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
|
|
||||||
file - linux system file utility to get mime-type
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
|
|
||||||
eanble from script manager
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
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 dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("recent_bookmarks"), -- visible name of script
|
|
||||||
purpose = _("add recently edited and exported images to system recent bookmarks"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "recent_bookmarks"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.info
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- format strings for writing the bookmarks
|
|
||||||
|
|
||||||
local BOOKMARK_FORMAT <const> = " <bookmark href=\"file://%s\" added=\"%s\" modified=\"%s\" visited=\"%s\">\n"
|
|
||||||
local MIME_FORMAT <const> = " <mime:mime-type type=\"%s\"/>\n"
|
|
||||||
local EXPORT_FORMAT <const> = " <bookmark:application name=\"xdg-open\" exec=\"'xdg-open %%u'\" modified=\"%s\" count=\"1\"/>\n"
|
|
||||||
local EDIT_FORMAT <const> = " <bookmark:application name=\"darktable\" exec=\"'darktable %%u'\" modified=\"%s\" count=\"1\"/>\n"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local recent_bookmarks = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
recent_bookmarks.log_level = DEFAULT_LOG_LEVEL
|
|
||||||
recent_bookmarks.bookmark_file = nil
|
|
||||||
recent_bookmarks.current_view = nil
|
|
||||||
recent_bookmarks.in_darkroom = false
|
|
||||||
recent_bookmarks.current_image = nil
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = recent_bookmarks
|
|
||||||
local rb = recent_bookmarks
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
-- helper functions
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
local function set_log_level(level)
|
|
||||||
if not level then
|
|
||||||
level = namespace.log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
local old_log_level = log.log_level()
|
|
||||||
log.log_level(level)
|
|
||||||
|
|
||||||
return old_log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_log_level(level)
|
|
||||||
if not level then
|
|
||||||
level = namespace.log_level
|
|
||||||
end
|
|
||||||
log.log_level(level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function reset_log_level()
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
namespace.log_level = DEFAULT_LOG_LEVEL
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pref_read(name, pref_type)
|
|
||||||
local old_log_level = set_log_level(namespace.log_level)
|
|
||||||
|
|
||||||
log.msg(log.debug, "name is " .. name .. " and type is " .. pref_type)
|
|
||||||
|
|
||||||
local val = dt.preferences.read(MODULE, name, pref_type)
|
|
||||||
|
|
||||||
log.msg(log.debug, "read value " .. tostring(val))
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pref_write(name, pref_type, value)
|
|
||||||
local old_log_level = set_log_level(namespace.log_level)
|
|
||||||
|
|
||||||
log.msg(log.debug, "writing value " .. tostring(value) .. " for name " .. name)
|
|
||||||
|
|
||||||
dt.preferences.write(MODULE, name, pref_type, value)
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------
|
|
||||||
-- program functions
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
local function zulu_timestamp()
|
|
||||||
return os.date("!%Y-%m-%dT%H:%M:%SZ")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_mimetype(filepath)
|
|
||||||
local mimetype = "application/octet-stream"
|
|
||||||
local p = io.popen("file -L --mime-type " .. df.sanitize_filename(filepath))
|
|
||||||
if p then
|
|
||||||
for line in p:lines() do
|
|
||||||
local result = string.match(line, ".-: (.+)$")
|
|
||||||
if result then
|
|
||||||
mimetype = result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return mimetype
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_bookmark_file()
|
|
||||||
local file = nil
|
|
||||||
local home = os.getenv("HOME")
|
|
||||||
if home then
|
|
||||||
if df.test_file(home .. PS ..".local/share/recently-used.xbel", "e") then
|
|
||||||
file = home .. PS ..".local/share/recently-used.xbel"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return file
|
|
||||||
end
|
|
||||||
|
|
||||||
local function write_bookmark_file(bookmark, mimetype, application)
|
|
||||||
local file = io.open(rb.bookmark_file, "r+")
|
|
||||||
if file then
|
|
||||||
local pos = file:seek("end", -7)
|
|
||||||
if pos > 0 then
|
|
||||||
file:write(bookmark)
|
|
||||||
file:write(" <info>\n <metadata owner=\"http://freedesktop.org\">\n")
|
|
||||||
file:write(mimetype)
|
|
||||||
file:write(" <bookmark:applications>\n")
|
|
||||||
file:write(application)
|
|
||||||
file:write(" </bookmark:applications>\n </metadata>\n </info>\n </bookmark>\n</xbel>")
|
|
||||||
file:close()
|
|
||||||
else
|
|
||||||
log.msg(log.screen, _("failed to open bookmark file"))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.msg(log.screen, _("failed to open bookmark file"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create_bookmark_string(filename, zulu)
|
|
||||||
return string.format(BOOKMARK_FORMAT, filename, zulu, zulu, zulu)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create_mimetype_string(mimetype)
|
|
||||||
return string.format(MIME_FORMAT, mimetype)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function process_bookmark(image, filename)
|
|
||||||
|
|
||||||
local zulu = zulu_timestamp()
|
|
||||||
local filepath = filename and filename or (image.path .. PS .. image.filename)
|
|
||||||
local format = filename and EXPORT_FORMAT or EDIT_FORMAT
|
|
||||||
local mimetype = get_mimetype(filepath)
|
|
||||||
local bookmark_string = create_bookmark_string(filepath, zulu)
|
|
||||||
local mime_string = create_mimetype_string(mimetype)
|
|
||||||
local app_string = string.format(format, zulu)
|
|
||||||
write_bookmark_file(bookmark_string, mime_string, app_string)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
if dt.configuration.running_os ~= "linux" then
|
|
||||||
log.msg(log.screen, script_data.metadata.name .. _(" only runs on Linux"))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
rb.bookmark_file = find_bookmark_file()
|
|
||||||
if not rb.bookmark_file then
|
|
||||||
log.msg(log.screen, _("recent bookmarks file not found"))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.destroy_event(MODULE, "intermediate-export-image")
|
|
||||||
dt.destroy_event(MODULE, "darkroom-image-loaded")
|
|
||||||
dt.destroy_event(MODULE, "darkroom-image-history-changed")
|
|
||||||
dt.destroy_event(MODULE, "view-changed")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "intermediate-export-image",
|
|
||||||
function(event, image, filename, format, storage)
|
|
||||||
process_bookmark(image, filename)
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "darkroom-image-loaded",
|
|
||||||
function(event, clean, image)
|
|
||||||
if clean then
|
|
||||||
rb.in_darkroom = true
|
|
||||||
rb.current_view = "darkroom"
|
|
||||||
rb.current_image = image
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "darkroom-image-history-changed",
|
|
||||||
function(event, image)
|
|
||||||
if image == rb.current_image then
|
|
||||||
process_bookmark(image)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "view-changed",
|
|
||||||
function(event, old_view, new_view)
|
|
||||||
rb.current_view = new_view.id
|
|
||||||
if rb.current_view == "darkroom" then
|
|
||||||
rb.in_darkroom = true
|
|
||||||
else
|
|
||||||
if rb.in_darkroom then
|
|
||||||
rb.in_darkroom = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
regenerate_thumbnails.lua - regenerate mipmap cache for selected images
|
|
||||||
|
|
||||||
Copyright (C) 2025 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
regenerate_thumbnails - regenerate mipmap cache for selected images
|
|
||||||
|
|
||||||
regenerate_thumbnails drops the cached thumbnail for each selected image
|
|
||||||
and generates a new thumbnail.
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
* enable the script in script_manager
|
|
||||||
* assign a shortcut, if desired, to apply the script by hovering
|
|
||||||
over a skull and using the shortcut to regenerate the thumbnail
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
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 dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("regenerate_thumbnails"), -- visible name of script
|
|
||||||
purpose = _("regenerate mipmap cache for selected images"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/development/lua.scripts.manual/scripts/official/regenerate_thumbnails" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "regenerate_thumbnails"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.info
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local regenerate_thumbnails = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = regenerate_thumbnails
|
|
||||||
local rt = regenerate_thumbnails
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
-- helper functions
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
local function set_log_level(level)
|
|
||||||
local old_log_level = log.log_level()
|
|
||||||
log.log_level(level)
|
|
||||||
return old_log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_log_level(level)
|
|
||||||
log.log_level(level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pref_read(name, pref_type)
|
|
||||||
local old_log_level = set_log_level(sm.log_level)
|
|
||||||
|
|
||||||
log.msg(log.debug, "name is " .. name .. " and type is " .. pref_type)
|
|
||||||
|
|
||||||
local val = dt.preferences.read(MODULE, name, pref_type)
|
|
||||||
|
|
||||||
log.msg(log.debug, "read value " .. tostring(val))
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pref_write(name, pref_type, value)
|
|
||||||
local old_log_level = set_log_level(sm.log_level)
|
|
||||||
|
|
||||||
log.msg(log.debug, "writing value " .. tostring(value) .. " for name " .. name)
|
|
||||||
|
|
||||||
dt.preferences.write(MODULE, name, pref_type, value)
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function stop_job()
|
|
||||||
rt.job.valid = false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function generate_thumbnails(images)
|
|
||||||
local has_job = false
|
|
||||||
|
|
||||||
if #images > 50 then
|
|
||||||
rt.job = dt.gui.create_job("regenerating thumbnails", true, stop_job)
|
|
||||||
has_job = true
|
|
||||||
end
|
|
||||||
|
|
||||||
for count, image in ipairs(images) do
|
|
||||||
image:drop_cache()
|
|
||||||
image:generate_cache(true, 1, 3)
|
|
||||||
if has_job then
|
|
||||||
if not rt.job.valid then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if count % 10 == 0 then
|
|
||||||
rt.job.percent = count / #images
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if has_job then
|
|
||||||
rt.job.valid = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function update_button_sensitivity()
|
|
||||||
if #dt.gui.action_images > 0 then
|
|
||||||
dt.gui.libs.image.set_sensitive(MODULE, true)
|
|
||||||
else
|
|
||||||
dt.gui.libs.image.set_sensitive(MODULE, false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.gui.libs.image.register_action(
|
|
||||||
MODULE,
|
|
||||||
_("generate thumbnails"),
|
|
||||||
function(event, images)
|
|
||||||
generate_thumbnails(images)
|
|
||||||
end,
|
|
||||||
_("generate thumbnails")
|
|
||||||
)
|
|
||||||
|
|
||||||
update_button_sensitivity()
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.destroy_event(MODULE, "selection-changed")
|
|
||||||
dt.destroy_event(MODULE, "mouse-over-image-changed")
|
|
||||||
dt.gui.libs.image.destroy_action(MODULE, "regenerate thumbnails")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, shortcut)
|
|
||||||
local images = dt.gui.action_images
|
|
||||||
if images then
|
|
||||||
generate_thumbnails(images)
|
|
||||||
end
|
|
||||||
end, "regenerate thumbnails"
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "selection-changed",
|
|
||||||
function(event)
|
|
||||||
update_button_sensitivity()
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "mouse-over-image-changed",
|
|
||||||
function(event, image)
|
|
||||||
if #dt.gui.selection() < 1 then
|
|
||||||
if image then
|
|
||||||
dt.gui.libs.image.set_sensitive(MODULE, true)
|
|
||||||
else
|
|
||||||
dt.gui.libs.image.set_sensitive(MODULE, false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -38,23 +38,10 @@ local du = require "lib/dtutils"
|
|||||||
|
|
||||||
du.check_min_api_version("7.0.0", "save_selection")
|
du.check_min_api_version("7.0.0", "save_selection")
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msg)
|
|
||||||
return gettext(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return data structure for script_manager
|
-- return data structure for script_manager
|
||||||
|
|
||||||
local script_data = {}
|
local script_data = {}
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("save selection"),
|
|
||||||
purpose = _("shortcuts providing multiple selection buffers"),
|
|
||||||
author = "Jérémy Rosen",
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/save_selection"
|
|
||||||
}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
script_data.destroy = nil -- function to destory the script
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
||||||
@@ -74,16 +61,16 @@ for i = 1, buffer_count do
|
|||||||
local saved_selection
|
local saved_selection
|
||||||
dt.register_event("save_selection save " .. i, "shortcut", function()
|
dt.register_event("save_selection save " .. i, "shortcut", function()
|
||||||
saved_selection = dt.gui.selection()
|
saved_selection = dt.gui.selection()
|
||||||
end, string.format(_("save to buffer %d"), i))
|
end, "save to buffer " .. i)
|
||||||
dt.register_event("save_selection restore " .. i, "shortcut", function()
|
dt.register_event("save_selection restore " .. i, "shortcut", function()
|
||||||
dt.gui.selection(saved_selection)
|
dt.gui.selection(saved_selection)
|
||||||
end, string.format(_("restore from buffer %d"), i))
|
end, "restore from buffer " .. i)
|
||||||
end
|
end
|
||||||
|
|
||||||
local bounce_buffer = {}
|
local bounce_buffer = {}
|
||||||
dt.register_event("save_selection switch", "shortcut", function()
|
dt.register_event("save_selection switch", "shortcut", function()
|
||||||
bounce_buffer = dt.gui.selection(bounce_buffer)
|
bounce_buffer = dt.gui.selection(bounce_buffer)
|
||||||
end, _("switch selection with temporary buffer"))
|
end, "switch selection with temporary buffer")
|
||||||
|
|
||||||
script_data.destroy = destroy
|
script_data.destroy = destroy
|
||||||
|
|
||||||
|
|||||||
@@ -1,224 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
scheduler.lua - provide scheduler services for multitasking
|
|
||||||
|
|
||||||
Copyright (C) 2024 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
scheduler - provide scheduler services for multitasking
|
|
||||||
|
|
||||||
scheduler implements a sinple first in first out (FIFO) scheduler
|
|
||||||
necessary for lua script multitasking.
|
|
||||||
|
|
||||||
Scripts voluntarily yield to the scheduler and they are placed at
|
|
||||||
the end of the run queue. The first item in the run queue is told
|
|
||||||
to resume.
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
start script from script_manager
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
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 dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "scheduler"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.info
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
local BROADCAST_MSG <const> = "broadcast"
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("9.4.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
dt.gettext.bindtextdomain(MODULE , dt.configuration.config_dir .. "/lua/locale/")
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = "scheduler", -- name of script
|
|
||||||
purpose = _("provide scheduler services for multitasking"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/scheduler/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local scheduler = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
scheduler.run_queue = {}
|
|
||||||
scheduler.message_queue = {}
|
|
||||||
scheduler.log_level = log.debug
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.preferences.register(MODULE, "target_time_slice_seconds", "float", "scheduler preferred time slice", "target time slice before interrupt", 1.0, 0.1, 1.0, 0.1)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local sch = scheduler
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function remove_from_run_queue()
|
|
||||||
|
|
||||||
log.log_level(sch.log_level)
|
|
||||||
|
|
||||||
if #sch.run_queue > 0 then
|
|
||||||
local module = sch.run_queue[1]
|
|
||||||
local message = sch.message_queue[1]
|
|
||||||
table.remove(sch.run_queue, 1)
|
|
||||||
table.remove(sch.message_queue, 1)
|
|
||||||
log.msg(log.info, "run queue length is " .. #sch.run_queue)
|
|
||||||
log.msg(log.debug, "sending " .. message .. " to " .. module)
|
|
||||||
dt.util.message(MODULE, module, message)
|
|
||||||
else
|
|
||||||
log.msg(log.warn, "run queue empty, nothing to resume")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function add_to_run_queue(module, message)
|
|
||||||
|
|
||||||
log.log_level(sch.log_level)
|
|
||||||
|
|
||||||
table.insert(sch.run_queue, module)
|
|
||||||
table.insert(sch.message_queue, message)
|
|
||||||
|
|
||||||
log.msg(log.info, "run queue length is " .. #sch.run_queue)
|
|
||||||
|
|
||||||
dt.control.sleep(500) -- allow time for waiting scripts to start
|
|
||||||
|
|
||||||
remove_from_run_queue()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function send_status(sender)
|
|
||||||
dt.util.message(MODULE, sender, "running, queue length is " .. #sch.run_queue)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.util.message(MODULE, BROADCAST_MSG, "starting")
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
-- put things to destroy (events, storages, etc) here
|
|
||||||
dt.util.message(MODULE, BROADCAST_MSG, "stopping")
|
|
||||||
dt.destroy_event(MODULE, "inter-script-communication")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.register_event(MODULE, "inter-script-communication",
|
|
||||||
function(event, sender,receiver, message)
|
|
||||||
|
|
||||||
log.log_level(sch.log_level)
|
|
||||||
|
|
||||||
if receiver == "scheduler" then
|
|
||||||
log.msg(log.debug, "got message from " .. sender .. " to " .. receiver .. " with message " .. message)
|
|
||||||
if string.match(message, "^yield") then
|
|
||||||
log.msg(log.debug, "got message " .. message .. " from " .. sender)
|
|
||||||
add_to_run_queue(sender, string.gsub(message, "yield", "resume"))
|
|
||||||
elseif message == "status" then
|
|
||||||
send_status(sender)
|
|
||||||
elseif messages == "done" then
|
|
||||||
-- do nothing
|
|
||||||
else
|
|
||||||
log.msg(log.warn, "unrecognized message " .. message)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
select_duplicates.lua - select duplicate images from a collection
|
|
||||||
|
|
||||||
Copyright (C) 2026 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
select_duplicates - select duplicate images from a collection
|
|
||||||
|
|
||||||
select_duplicates adds a button to the select module to select
|
|
||||||
duplicate images from a collection. If the selection2collection
|
|
||||||
script is also active an option is available to have the selected
|
|
||||||
duplicates appear as a temporary collection.
|
|
||||||
|
|
||||||
A shortcut is available to select the duplicates from the collection
|
|
||||||
but it is somewhat slower because it doesn't have access to the current
|
|
||||||
image cache and therfore has to open each image separately.
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
|
|
||||||
* enable in script manager
|
|
||||||
* Press the button
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
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 dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("select duplicates"), -- visible name of script
|
|
||||||
purpose = _("select duplicate images from a collection"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/select_duplicates/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "select_duplicates"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.info
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local select_duplicates = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
select_duplicates.log_level = DEFAULT_LOG_LEVEL
|
|
||||||
select_duplicates.create_collection = false
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.preferences.register(MODULE, "create_collection", "bool",
|
|
||||||
script_data.metadata.name .. _(" create collection from selection"),
|
|
||||||
_("create collection of selected duplicates"), false)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = select_duplicates
|
|
||||||
local sd = select_duplicates
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
-- helper functions
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
local function set_log_level(level)
|
|
||||||
if not level then
|
|
||||||
level = namespace.log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
local old_log_level = log.log_level()
|
|
||||||
log.log_level(level)
|
|
||||||
|
|
||||||
return old_log_level
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_log_level(level)
|
|
||||||
if not level then
|
|
||||||
level = namespace.log_level
|
|
||||||
end
|
|
||||||
log.log_level(level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function reset_log_level()
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
namespace.log_level = DEFAULT_LOG_LEVEL
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pref_read(name, pref_type)
|
|
||||||
local old_log_level = set_log_level(namespace.log_level)
|
|
||||||
|
|
||||||
log.msg(log.debug, "name is " .. name .. " and type is " .. pref_type)
|
|
||||||
|
|
||||||
local val = dt.preferences.read(MODULE, name, pref_type)
|
|
||||||
|
|
||||||
log.msg(log.debug, "read value " .. tostring(val))
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pref_write(name, pref_type, value)
|
|
||||||
local old_log_level = set_log_level(namespace.log_level)
|
|
||||||
|
|
||||||
log.msg(log.debug, "writing value " .. tostring(value) .. " for name " .. name)
|
|
||||||
|
|
||||||
dt.preferences.write(MODULE, name, pref_type, value)
|
|
||||||
|
|
||||||
restore_log_level(old_log_level)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function select_duplicate_files(event, images)
|
|
||||||
local duplicates = {}
|
|
||||||
local sorted_files = {}
|
|
||||||
sd.create_collection = pref_read("create_collection", "bool")
|
|
||||||
|
|
||||||
for _, img in ipairs(images) do
|
|
||||||
local rawname = img.path .. PS .. img.filename
|
|
||||||
if not sorted_files[rawname] then
|
|
||||||
sorted_files[rawname] = {}
|
|
||||||
end
|
|
||||||
table.insert(sorted_files[rawname], img)
|
|
||||||
end
|
|
||||||
for k,v in pairs(sorted_files) do
|
|
||||||
if #v > 1 then
|
|
||||||
local old_image = nil
|
|
||||||
for _,i in ipairs(v) do
|
|
||||||
if not old_image then
|
|
||||||
old_image = i
|
|
||||||
else
|
|
||||||
if i.duplicate_index > old_image.duplicate_index then
|
|
||||||
table.insert(duplicates, i)
|
|
||||||
else
|
|
||||||
table.insert(duplicates, old_image)
|
|
||||||
old_image = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if sd.create_collection then
|
|
||||||
dt.util.message(MODULE, "selection2collection", "create")
|
|
||||||
end
|
|
||||||
return duplicates
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.gui.libs.select.register_selection(MODULE, _("select duplicates"), select_duplicate_files,
|
|
||||||
_("select duplicated images"))
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.gui.libs.select.destroy_selection(MODULE)
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
if not dt.query_event(MODULE, "shortcut") then -- skip if shortcut already registered
|
|
||||||
dt.register_event(MODULE, "shortcut",
|
|
||||||
function(event, shortcut)
|
|
||||||
local duplicates = select_duplicate_files(event, dt.collection)
|
|
||||||
if duplicates then
|
|
||||||
dt.gui.selection(duplicates)
|
|
||||||
end
|
|
||||||
end, _("select duplicates")
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
select_raw_non_raw.lua - select raw or non raw files from a collection
|
|
||||||
|
|
||||||
Copyright (C) 2025 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
select_raw_non_raw - select raw or non raw files from a collection
|
|
||||||
|
|
||||||
select_raw_non_raw adds two selection buttons, one to select raw
|
|
||||||
images and one to select non raw images.
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
* enable the script with script_manager
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
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 dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "select_raw_non_raw"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.warn
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = "select_raw_non_raw", -- name of script
|
|
||||||
purpose = _("select raw or non raw files from collection"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/select_raw_non_raw" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local select_group_followers = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = select_group_followers
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function select_raw_files(event, images)
|
|
||||||
local raws = {}
|
|
||||||
|
|
||||||
for _, img in ipairs(images) do
|
|
||||||
if img.is_raw then
|
|
||||||
table.insert(raws, img)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return raws
|
|
||||||
end
|
|
||||||
|
|
||||||
local function select_non_raw_files(event, images)
|
|
||||||
local non_raws = {}
|
|
||||||
|
|
||||||
for _, img in ipairs(images) do
|
|
||||||
if not img.is_raw then
|
|
||||||
table.insert(non_raws, img)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return non_raws
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
dt.gui.libs.select.register_selection(MODULE .. "_raws", _("select raw"), select_raw_files,
|
|
||||||
_("select raw files"))
|
|
||||||
dt.gui.libs.select.register_selection(MODULE .. "_non_raws", _("select non raw"), select_non_raw_files,
|
|
||||||
_("select non raw files"))
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
-- put things to destroy (events, storages, etc) here
|
|
||||||
dt.gui.libs.select.destroy_selection(MODULE .. "_raws")
|
|
||||||
dt.gui.libs.select.destroy_selection(MODULE .. "_non_raws")
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
--[[
|
|
||||||
|
|
||||||
select_unaltered.lua - select images that have not been altered in any way
|
|
||||||
|
|
||||||
Copyright (C) 2026 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/>.
|
|
||||||
]]
|
|
||||||
--[[
|
|
||||||
select_unaltered - select images that have not been altered in any way
|
|
||||||
|
|
||||||
select_unaltered selects images from the current collection that have not
|
|
||||||
been altered in any way. In other words just imported into darktable, not
|
|
||||||
loaded into darkroom nor having any style applied.
|
|
||||||
|
|
||||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
|
||||||
None
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
* enable with script_manager
|
|
||||||
|
|
||||||
BUGS, COMMENTS, SUGGESTIONS
|
|
||||||
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 dtsys = require "lib/dtutils.system"
|
|
||||||
local log = require "lib/dtutils.log"
|
|
||||||
-- local debug = require "darktable.debug"
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A P I C H E C K
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
du.check_min_api_version("7.0.0", MODULE) -- choose the minimum version that contains the features you need
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- I 1 8 N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local gettext = dt.gettext.gettext
|
|
||||||
|
|
||||||
local function _(msgid)
|
|
||||||
return gettext(msgid)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- S C R I P T M A N A G E R I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local script_data = {}
|
|
||||||
|
|
||||||
script_data.destroy = nil -- function to destory the script
|
|
||||||
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
|
|
||||||
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
|
|
||||||
script_data.show = nil -- only required for libs since the destroy_method only hides them
|
|
||||||
|
|
||||||
script_data.metadata = {
|
|
||||||
name = _("select_unaltered"), -- visible name of script
|
|
||||||
purpose = _("select images that have not been altered in any way"), -- purpose of script
|
|
||||||
author = "Bill Ferguson <wpferguson@gmail.com>", -- your name and optionally e-mail address
|
|
||||||
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/official/select_unaltered/" -- URL to help/documentation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- C O N S T A N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local MODULE <const> = "select_unaltered"
|
|
||||||
local DEFAULT_LOG_LEVEL <const> = log.info
|
|
||||||
local TMP_DIR <const> = dt.configuration.tmp_dir
|
|
||||||
|
|
||||||
-- path separator
|
|
||||||
local PS <const> = dt.configuration.running_os == "windows" and "\\" or "/"
|
|
||||||
|
|
||||||
-- command separator
|
|
||||||
local CS <const> = dt.configuration.running_os == "windows" and "&" or ";"
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- L O G L E V E L
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
log.log_level(DEFAULT_LOG_LEVEL)
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- N A M E S P A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local select_unaltered = {}
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- G L O B A L V A R I A B L E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
select_unaltered.log_level = DEFAULT_LOG_LEVEL
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- P R E F E R E N C E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- A L I A S E S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local namespace = select_unaltered
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- F U N C T I O N S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function select_unaltered_files(event, images)
|
|
||||||
local unaltered = {}
|
|
||||||
|
|
||||||
for _, img in ipairs(images) do
|
|
||||||
if not img.is_altered then
|
|
||||||
table.insert(unaltered, img)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return unaltered
|
|
||||||
end
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- M A I N P R O G R A M
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- U S E R I N T E R F A C E
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
dt.gui.libs.select.register_selection(MODULE, _("select unaltered"), select_unaltered_files,
|
|
||||||
_("select unaltered files"))
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- D A R K T A B L E I N T E G R A T I O N
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
local function destroy()
|
|
||||||
dt.gui.libs.select.destroy_selection(MODULE)
|
|
||||||
end
|
|
||||||
|
|
||||||
script_data.destroy = destroy
|
|
||||||
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
-- E V E N T S
|
|
||||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
return script_data
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user