mail2blog.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. --[[ Export image for blog, duplicate and set it LOCKED ]]
  2. local script_path = debug.getinfo(1, "S").source:sub(2):match("(.*/)")
  3. package.path = script_path .. "?.lua;" .. package.path
  4. local config = require("config")
  5. email_1 = config.email_1
  6. email_2 = config.email_2
  7. email_3 = config.email_3
  8. local image_max_x = 2500
  9. local image_max_y = 2500
  10. -- local jpg_quality_str = '92'
  11. -- module name
  12. local MODULE_NAME = "mail2blog"
  13. local df = require "lib/dtutils.file"
  14. local dt = require "darktable"
  15. local du = require "lib/dtutils"
  16. local log = require 'lib/dtutils.log'
  17. local dtsys = require "lib/dtutils.system"
  18. local function quote(text)
  19. return '"' .. text .. '"'
  20. end
  21. local charset = {} do -- [0-9a-zA-Z]
  22. for c = 48, 57 do table.insert(charset, string.char(c)) end
  23. for c = 65, 90 do table.insert(charset, string.char(c)) end
  24. for c = 97, 122 do table.insert(charset, string.char(c)) end
  25. end
  26. -- - - - - - - - - - - - - - - - - - - - - - - -
  27. -- V E R S I O N C H E C K
  28. -- - - - - - - - - - - - - - - - - - - - - - - -
  29. du.check_min_api_version("5.0.2", MODULE_NAME) -- darktable 3.0
  30. -- script_manager integration to allow a script to be removed
  31. -- without restarting darktable
  32. local function destroy()
  33. -- nothing to destroy
  34. end
  35. -- - - - - - - - - - - - - - - - - - - - - - - -
  36. -- C O N S T A N T S
  37. -- - - - - - - - - - - - - - - - - - - - - - - -
  38. local PS = dt.configuration.running_os == "windows" and "\\" or "/"
  39. -- - - - - - - - - - - - - - - - - - - - - - - -
  40. -- T R A N S L A T I O N S
  41. -- - - - - - - - - - - - - - - - - - - - - - - -
  42. local gettext = dt.gettext
  43. gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
  44. local function _(msgid)
  45. return gettext.dgettext(MODULE_NAME, msgid)
  46. end
  47. -- - - - - - - - - - - - - - - - - - - - - - - -
  48. -- M A I N
  49. -- - - - - - - - - - - - - - - - - - - - - - - -
  50. -- alias dt.control.sleep to sleep
  51. local sleep = dt.control.sleep
  52. ---------------------------------------------------------------
  53. -- some helper methods to log information messages
  54. log.log_level(log.info) -- log.info or log.warn or log.debug
  55. local LogCurrentStep = ''
  56. local LogMajorNr = 0
  57. local LogMajorMax = 0
  58. local LogSummaryMessages = {}
  59. local function GetLogInfoText(text)
  60. return '[' .. LogMajorNr .. '/' .. LogMajorMax .. '] ' .. LogCurrentStep .. ': ' .. text
  61. end
  62. local function LogInfo(text)
  63. log.msg(log.info, GetLogInfoText(text))
  64. end
  65. local function LogScreen(text)
  66. log.msg(log.screen, text)
  67. end
  68. local function LogSummaryClear()
  69. for k, v in pairs(LogSummaryMessages) do
  70. LogSummaryMessages[k] = nil
  71. end
  72. end
  73. local function LogSummaryMessage(text)
  74. table.insert(LogSummaryMessages, GetLogInfoText(text))
  75. end
  76. -- ----------------------------------------------------
  77. local function set_published_tag ( email, image )
  78. if email == 'postie_markus_spring.info@markus-spring.de' then
  79. local tagnr = dt.tags.find('photography|published|blog')
  80. dt.tags.attach(tagnr,image)
  81. elseif email == 'postie_vhs_fotogruppe_reichenhall@markus-spring.de' then
  82. local tagnr = dt.tags.find('photography|published|vhs-fotogruppe')
  83. dt.tags.attach(tagnr,image)
  84. -- else
  85. -- local tagnr = dt.tags.find('photography|published|instagram')
  86. -- dt.tags.attach(tagnr,image)
  87. end
  88. local tagnr = dt.tags.find('LOCKED')
  89. dt.tags.attach(tagnr,image)
  90. end
  91. local function getPureFilename(path)
  92. return string.match(path, "([^/\\]+)$")
  93. end
  94. local function set_metadata_note ( tmp_exported, image )
  95. if image then
  96. local current_notes = image.notes or "" -- Retrieve existing notes or set as empty
  97. if current_notes ~= "" then
  98. current_notes = current_notes .. "; "
  99. end
  100. image.notes = current_notes .. 'published as ' .. getPureFilename(tmp_exported)
  101. end
  102. end
  103. local function set_rating_min_2 ( image )
  104. if image.rating < 2 then
  105. image.rating = 2
  106. end
  107. end
  108. local function get_executable( binaryname, binarystring )
  109. local binary = dt.preferences.read(MODULE_NAME, binaryname, "string")
  110. if binary == "" then
  111. dt.print(_( binarystring .. " executable not configured"))
  112. return
  113. end
  114. binary = df.sanitize_filename(binary)
  115. return binary
  116. end
  117. local function run_exiftool ( file, tmp_exported, flags )
  118. local exiftoolbinary = get_executable("exiftoolbinary", "exiftool")
  119. run_cmd = exiftoolbinary..' -TagsFromFile '..file..' '..flags..' '..tmp_exported
  120. LogInfo(string.format("Running %s", run_cmd))
  121. local job = dt.gui.create_job(string.format("Running %s", run_cmd), true, stop_job)
  122. resp = dtsys.external_command(run_cmd)
  123. job.valid = false
  124. end
  125. local function isempty(s)
  126. return s == nil or s == ''
  127. end
  128. ---------------------------------------------------------------
  129. -- helper functions to access darktable feature via user interface
  130. -- use event handling helper functions to wait for pixel pipe
  131. -- processing to complete
  132. local function numberToString(number, nilReplacement, nanReplacement)
  133. -- convert given number to string
  134. -- return 'not a number' and 'nil' as '0/0'
  135. -- log output equals to dt.gui.action command and parameters
  136. if (number ~= number) then
  137. return nanReplacement or '0/0'
  138. end
  139. if (number == nil) then
  140. return nilReplacement or '0/0'
  141. end
  142. -- some digits with dot
  143. local result = string.format('%.4f', number)
  144. result = string.gsub(result, ',', '.')
  145. return result
  146. end
  147. -- Function to replace German umlauts and sanitize the filename
  148. local function create_safe_filename(title)
  149. local replacements = {
  150. ["ä"] = "ae", ["ö"] = "oe", ["ü"] = "ue",
  151. ["Ä"] = "Ae", ["Ö"] = "Oe", ["Ü"] = "Ue",
  152. ["ß"] = "ss"
  153. }
  154. title = title:gsub("[%z\1-\127\194-\244][\128-\191]*", function(char)
  155. return replacements[char] or char
  156. end)
  157. title = title:gsub("[^%w%-]", "_")
  158. title = title:gsub("_+", "_")
  159. title = title:gsub("^_+", ""):gsub("_+$", "")
  160. if title == "" then
  161. title = "untitled"
  162. end
  163. title = title:sub(1, 255)
  164. return title
  165. end
  166. -- ----------------------------------------------------
  167. dt.print_log(MODULE_NAME .. ' loaded')
  168. -- save the configuration
  169. local current_view = dt.gui.current_view()
  170. local select_publication_target = dt.new_widget("combobox")
  171. {
  172. label = "target",
  173. tooltip = "Select blog for image publication",
  174. changed_callback = function(w) dt.preferences.write(MODULE_NAME, "target", "string", w.selected) end,
  175. email_1, email_2, email_3
  176. }
  177. local publication_button = dt.new_widget("button")
  178. {
  179. label = "Publish!",
  180. clicked_callback = function ()
  181. for i_, i in ipairs(dt.gui.action_images) do
  182. if isempty(i.title) then
  183. local job = dt.gui.create_job("FEHLER: Das Bild " .. i.filename .. " hat keinen Titel.", true, stop_job)
  184. os.execute("sleep " .. 3)
  185. job.valid = false
  186. else
  187. set_rating_min_2( i )
  188. -- create duplicate
  189. local newimg = i.duplicate_with_history(i)
  190. local jpeg_exporter = dt.new_format("jpeg")
  191. jpeg_exporter.max_height = image_max_y
  192. jpeg_exporter.max_width = image_max_x
  193. -- local tmp_exported = os.tmpname()..".jpg"
  194. local tempname = os.tmpname()
  195. os.remove(tempname)
  196. os.execute("mkdir " .. tempname)
  197. local tmp_exported = tempname .. '/' .. create_safe_filename(i.title) .. '.jpg'
  198. local job = dt.gui.create_job(string.format(_("Converting raw file '%s' to jpeg..."), i.filename), true, stop_job)
  199. jpeg_exporter:write_image(i, tmp_exported, false)
  200. -- copy exif data from original file
  201. run_exiftool( df.sanitize_filename(i.path..PS..i.filename), tmp_exported, '-exif:all --subifd:all --Orientation -overwrite_original' )
  202. -- copy exif data from xmp file
  203. run_exiftool( df.sanitize_filename(i.sidecar) , tmp_exported, '-xmp:all -exif:all --subifd:all -overwrite_original' )
  204. -- run mailscript
  205. local spring2lifescript = get_executable("spring2lifescript", "spring2life script")
  206. run_cmd = spring2lifescript .. " " .. tmp_exported
  207. .. ' ' .. select_publication_target.value
  208. .. ' ' .. df.sanitize_filename(i.filename) .. ' &'
  209. LogInfo(run_cmd)
  210. job = false
  211. local job = dt.gui.create_job("Running " .. run_cmd, true, stop_job)
  212. os.execute(run_cmd)
  213. set_published_tag ( select_publication_target.value, i )
  214. job = false
  215. -- set_metadata_note ( tmp_exported, i )
  216. LogScreen("Done")
  217. --- dt.styles.delete(tmpstyle)
  218. end
  219. end
  220. end
  221. }
  222. local lib_widgets = {}
  223. table.insert(lib_widgets, select_publication_target)
  224. table.insert(lib_widgets, publication_button)
  225. -- ... and tell dt about it all
  226. dt.register_lib(
  227. MODULE_NAME, -- plugin name
  228. MODULE_NAME, -- name
  229. true, -- expandable
  230. false, -- resetable
  231. {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
  232. dt.new_widget("box") -- widget
  233. {
  234. orientation = "vertical",
  235. -- sensitive = enfuse_installed,
  236. table.unpack(lib_widgets)
  237. },
  238. nil,-- view_enter
  239. nil -- view_leave
  240. )
  241. -- end
  242. -- -- register the new preferences -----------------------------------------------
  243. dt.preferences.register(MODULE_NAME, "exiftoolbinary", "file",
  244. _(MODULE_NAME .. ": executable for exiftool"),
  245. _("select executable for exiftool command line version") , "")
  246. dt.preferences.register(MODULE_NAME, "spring2lifescript", "file",
  247. _(MODULE_NAME .. ": executable for mail2spring2life script"),
  248. _("select executable for mail2spring2life script") , "")
  249. -- ----------------------------------------------------------------------------------
  250. -- set the destroy routine so that script_manager can call it when
  251. -- it's time to destroy the script and then return the data to
  252. -- script_manager
  253. local script_data = {}
  254. script_data.destroy = destroy
  255. return script_data