mail2blog.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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. local function remove_unneeded_exif_values ( jpeg_file )
  129. local tags_to_remove = {
  130. "ModifyDate", "DateTimeOriginal", "CreateDate", "DateCreated",
  131. "TimeCreated", "GPSTimeStamp", "GPSDateStamp", "GPSDateTime",
  132. "IFD0:*", "IFD1:*"
  133. }
  134. local args = {"-overwrite_original"}
  135. for _, tag in ipairs(tags_to_remove) do
  136. table.insert(args, "-" .. tag .. "=")
  137. end
  138. table.insert(args, "-FileModifyDate<FileModifyDate")
  139. table.insert(args, filename)
  140. --run_exiftool( jpeg_file .. " " .. table.concat(args, " ") )
  141. local exiftoolbinary = get_executable("exiftoolbinary", "exiftool")
  142. run_cmd = exiftoolbinary..' '..jpeg_file..' '.. table.concat(args, " ")
  143. LogInfo(string.format("Running %s", run_cmd))
  144. local job = dt.gui.create_job(string.format("Running %s", run_cmd), true, stop_job)
  145. resp = dtsys.external_command(run_cmd)
  146. job.valid = false
  147. return nil
  148. end
  149. -- Function to replace German umlauts and sanitize the filename
  150. local function create_safe_filename(title)
  151. local replacements = {
  152. ["ä"] = "ae", ["ö"] = "oe", ["ü"] = "ue",
  153. ["Ä"] = "Ae", ["Ö"] = "Oe", ["Ü"] = "Ue",
  154. ["ß"] = "ss"
  155. }
  156. title = title:gsub("[%z\1-\127\194-\244][\128-\191]*", function(char)
  157. return replacements[char] or char
  158. end)
  159. title = title:gsub("[^%w%-]", "_")
  160. title = title:gsub("_+", "_")
  161. title = title:gsub("^_+", ""):gsub("_+$", "")
  162. if title == "" then
  163. title = "untitled"
  164. end
  165. title = title:sub(1, 255)
  166. return title
  167. end
  168. -- ----------------------------------------------------
  169. dt.print_log(MODULE_NAME .. ' loaded')
  170. -- save the configuration
  171. local current_view = dt.gui.current_view()
  172. local select_publication_target = dt.new_widget("combobox")
  173. {
  174. label = "target",
  175. tooltip = "Select blog for image publication",
  176. changed_callback = function(w) dt.preferences.write(MODULE_NAME, "target", "string", w.selected) end,
  177. email_1, email_2, email_3
  178. }
  179. local publication_button = dt.new_widget("button")
  180. {
  181. label = "Publish!",
  182. clicked_callback = function ()
  183. for i_, i in ipairs(dt.gui.action_images) do
  184. if isempty(i.title) then
  185. local job = dt.gui.create_job("FEHLER: Das Bild " .. i.filename .. " hat keinen Titel.", true, stop_job)
  186. os.execute("sleep " .. 3)
  187. job.valid = false
  188. else
  189. set_rating_min_2( i )
  190. -- create duplicate
  191. local newimg = i.duplicate_with_history(i)
  192. local jpeg_exporter = dt.new_format("jpeg")
  193. jpeg_exporter.max_height = image_max_y
  194. jpeg_exporter.max_width = image_max_x
  195. -- local tmp_exported = os.tmpname()..".jpg"
  196. local tempname = os.tmpname()
  197. os.remove(tempname)
  198. os.execute("mkdir " .. tempname)
  199. local tmp_exported = tempname .. '/' .. create_safe_filename(i.title) .. '.jpg'
  200. local job = dt.gui.create_job(string.format(_("Converting raw file '%s' to jpeg..."), i.filename), true, stop_job)
  201. jpeg_exporter:write_image(i, tmp_exported, false)
  202. -- copy exif data from original file
  203. run_exiftool( df.sanitize_filename(i.path..PS..i.filename), tmp_exported, '-exif:all --subifd:all --Orientation -overwrite_original' )
  204. -- copy exif data from xmp file
  205. run_exiftool( df.sanitize_filename(i.sidecar) , tmp_exported, '-xmp:all -exif:all --subifd:all -overwrite_original' )
  206. remove_unneeded_exif_values(tmp_exported)
  207. -- run mailscript
  208. local spring2lifescript = get_executable("spring2lifescript", "spring2life script")
  209. run_cmd = spring2lifescript .. " " .. tmp_exported
  210. .. ' ' .. select_publication_target.value
  211. .. ' ' .. df.sanitize_filename(i.filename) .. ' &'
  212. LogInfo(run_cmd)
  213. job = false
  214. local job = dt.gui.create_job("Running " .. run_cmd, true, stop_job)
  215. os.execute(run_cmd)
  216. set_published_tag ( select_publication_target.value, i )
  217. job = false
  218. -- set_metadata_note ( tmp_exported, i )
  219. LogScreen("Done")
  220. --- dt.styles.delete(tmpstyle)
  221. end
  222. end
  223. end
  224. }
  225. local lib_widgets = {}
  226. table.insert(lib_widgets, select_publication_target)
  227. table.insert(lib_widgets, publication_button)
  228. -- ... and tell dt about it all
  229. dt.register_lib(
  230. MODULE_NAME, -- plugin name
  231. MODULE_NAME, -- name
  232. true, -- expandable
  233. false, -- resetable
  234. {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
  235. dt.new_widget("box") -- widget
  236. {
  237. orientation = "vertical",
  238. -- sensitive = enfuse_installed,
  239. table.unpack(lib_widgets)
  240. },
  241. nil,-- view_enter
  242. nil -- view_leave
  243. )
  244. -- end
  245. -- -- register the new preferences -----------------------------------------------
  246. dt.preferences.register(MODULE_NAME, "exiftoolbinary", "file",
  247. _(MODULE_NAME .. ": executable for exiftool"),
  248. _("select executable for exiftool command line version") , "")
  249. dt.preferences.register(MODULE_NAME, "spring2lifescript", "file",
  250. _(MODULE_NAME .. ": executable for mail2spring2life script"),
  251. _("select executable for mail2spring2life script") , "")
  252. -- ----------------------------------------------------------------------------------
  253. -- set the destroy routine so that script_manager can call it when
  254. -- it's time to destroy the script and then return the data to
  255. -- script_manager
  256. local script_data = {}
  257. script_data.destroy = destroy
  258. return script_data