lsyncd/docs/manual/examples/auto-image-magic/index.md

7.2 KiB

layout title tab
default Example: Auto-Image-Magic manual/examples

This example is a layer 1 script to make a special "magic" directory in which image files will be converted automatically therein.

The full script:

{% highlight lua %} local formats = { jpg = true, gif = true, png = true }

convert = { delay = 0,

maxProcesses = 99,

action = function(inlet)
	local event = inlet.getEvent()

	if event.isdir then
		-- ignores events on dirs
		inlet.discardEvent(event)
		return
	end

	-- extract extension and basefilename
	local p    = event.pathname
	local ext  = string.match(p, ".*%.([^.]+)$")
	local base = string.match(p, "(.*)%.[^.]+$")
	if not formats[ext] then
		-- an unknown extenion
		log("Normal", "not doing something on ."..ext)
		inlet.discardEvent(event)
		return
	end

	-- autoconvert on create and modify
	if event.etype == "Create" or event.etype == "Modify" then
		-- builds one bash command
		local cmd = ""
		-- do for all other extensions
		for k, _ in pairs(formats) do
			if k ~= ext then
				-- excludes files to be created, so no
				-- followup actions will occur
				inlet.addExclude(base..'.'..k)
				if cmd ~= ""  then
					cmd = cmd .. " && "
				end
				cmd = cmd..
					'/usr/bin/convert "'..
					event.source..p..'" "'..
					event.source..base..'.'..k..
					'" || /bin/true'
			end
		end
		log("Normal", "Converting "..p)
		spawnShell(event, cmd)
		return
	end

	-- deletes all formats if you delete one
	if event.etype == "Delete" then
		-- builds one bash command
		local cmd = ""
		-- do for all other extensions
		for k, _ in pairs(formats) do
			if k ~= ext then
				-- excludes files to be deleted, so no
				-- followup actions will occur
				inlet.addExclude(base..'.'..k)
				if cmd ~= ""  then
					cmd = cmd .. " && "
				end
				cmd = cmd..
					'rm "'..event.source..base..'.'..k..
					'" || /bin/true'
			end
		end
		log("Normal", "Deleting all "..p)
		spawnShell(event, cmd)
		return
	end

	-- ignores other events.
	inlet.discardEvent(event)
end,

-----
-- Removes excludes when convertions are finished
--
collect = function(event, exitcode)
	local p     = event.pathname
	local ext   = string.match(p, ".*%.([^.]+)$")
	local base  = string.match(p, "(.*)%.[^.]+$")
	local inlet = event.inlet

	if event.etype == "Create" or
	   event.etype == "Modify" or
	   event.etype == "Delete"
	then
		for k, _ in pairs(formats) do
			inlet.rmExclude(base..'.'..k)
		end
	end
end,

}

sync{convert, source="magicdir", recursive=false} {% endhighlight %}

This creates a local table of all supported file formats. The file formats are used as keys.

{% highlight lua %} local formats = { jpg=true, gif=true, png=true, } {% endhighlight %}

Configures actions to be instant and there is unlimits the amount the conversion to be done at once. Well not unlimited but set the limit pretty high.

{% highlight lua %} convert = { delay = 0, maxProcesses = 99, {% endhighlight %}

This script uses the layer 1 inlet interface altough it greps only single events and not lists. It does this instead of layer 2 as it needs to do common operations for all kind of events.

{% highlight lua %} action = function(inlet) local event = inlet.getEvent() {% endhighlight %}

Ignores directories. As using layer 1 it has to explicitly discard events it does not spawn actions for.

{% highlight lua %} if event.isdir then -- ignores events on dirs inlet.discardEvent(event) return end {% endhighlight %}

Uses Lua string patterns to extract the file extension from the rest - here called base.

{% highlight lua %} -- extract extension and basefilename local p = event.pathname local ext = string.match(p, ".%.([^.]+)$") local base = string.match(p, "(.)%.[^.]+$") {% endhighlight %}

Looks the extension up in the formats table. This can be done, since formats are keys in that table. If not an image format it bails out.

{% highlight lua %} if not formats[ext] then -- an unknown extenion log("Normal", "not doing something on ."..ext) inlet.discardEvent(event) return end {% endhighlight %}

Following actions will done on "Create" and "Modify" events.

{% highlight lua %} -- autoconvert on create and modify if event.etype == "Create" or event.etype == "Modify" then {% endhighlight %}

This script builds a bash command using a string.

{% highlight lua %} -- builds one bash command local cmd = "" {% endhighlight %}

It iterates for all image formats and excludes the one which is the source image.

{% highlight lua %} -- do for all other extensions for k, _ in pairs(formats) do if k ~= ext then {% endhighlight %}

This is a little trick. It creates Exclusions for the converted images. As this images are not placed in a target directory but right next to the source image in the source directory they would otherwise trigger Create actions as well.

{% highlight lua %} -- excludes files to be created, so no -- followup actions will occur inlet.addExclude(base..'.'..k) {% endhighlight %}

And for every image to be converted adds the calls to the arguments. It uses " || /bin/true " to let the shell continue if one conversion fails. In that it chains the conversion with '&&' they will be called sequentially.

{% highlight lua %} if cmd ~= "" then cmd = cmd .. " && " end cmd = cmd.. '/usr/bin/convert "'.. event.source..p..'" "'.. event.source..base..'.'..k.. '" || /bin/true' {% endhighlight %}

And eventually it spawns the shell doing the conversions and is finished.

{% highlight lua %} end end log("Normal", "Converting "..p) spawnShell(event, cmd) return end {% endhighlight %}

For deletions it does technically something similar, but it deletes all other file formats of the image.

{% highlight lua %} -- deletes all formats if you delete one if event.etype == "Delete" then -- builds one bash command local cmd = "" -- do for all other extensions for k, _ in pairs(formats) do if k ~= ext then -- excludes files to be deleted, so no -- followup actions will occur inlet.addExclude(base..'.'..k) if cmd ~= "" then cmd = cmd .. " && " end cmd = cmd.. 'rm "'..event.source..base..'.'..k.. '" || /bin/true' end end log("Normal", "Deleting all "..p) spawnShell(event, cmd) return end {% endhighlight %}

and not to forget to nicely discard all other events.

{% highlight lua %} -- ignores other events. inlet.discardEvent(event) end, {% endhighlight %}

collect is called when the conversions finished. It will remove the temporary excludes again.

{% highlight lua %} ----- -- Removes excludes when convertions are finished -- collect = function(event, exitcode) local p = event.pathname local ext = string.match(p, ".%.([^.]+)$") local base = string.match(p, "(.)%.[^.]+$") local inlet = event.inlet

	if event.etype == "Create" or
	   event.etype == "Modify" or
	   event.etype == "Delete"
	then
		for k, _ in pairs(formats) do
			inlet.rmExclude(base..'.'..k)
		end
	end
end,

{% endhighlight %}

And finally use the configuration to watch "magicdir".

{% highlight lua %} sync{convert, source="magicdir", recursive=false} {% endhighlight %}