Play CLI

Play CLI defines helpers to deal with UNIX command with Play Framework iteratees.

Unix Pipe

cat list.txt | wc | mail -s "Count" gre@greweb.fr

Iteratees

val chunkedWaveStream = rawStream &> chunker &> waveEncoder

&> very similar to |

Unix Pipe + Iteratees

val chunkedAudioStream = rawStream &> CLI.pipe("sox -t raw - -t ogg -")

Under the hood...

scala.sys.Process

Play CLI relies on Process to create commands.

play.api.libs.iteratee

Play CLI is implemented using Iteratee paradigm.

3 use-cases

Enumerate: creates a stream from a command which generates output

def enumerate (cmd: ProcessBuilder) : Enumerator[Array[Byte]]

Pipe: pipes a command which consumes input and generates output

def pipe (command: ProcessBuilder) : Enumeratee[Array[Byte], Array[Byte]]

Consume: creates a process which consumes a stream

def consume (command: ProcessBuilder) : Iteratee[Array[Byte], Int]

Examples

Find files

val find = CLI.enumerate("find .") Ok.stream(find)

Simple.

Observe a log file

val tail = CLI.enumerate("tail -f /var/log/nginx/access.log") val (sharedTail, _) = Concurrent.broadcast(tail) def stream = Action(Ok.stream(sharedTail))

ImageMagick is your friend

val convert14colors = CLI.pipe("convert - -colors 14 png:-") Ok.stream( Enumerator.fromFile("nyancat.jpg") &> convert14colors)

Image stream processing: Easy!

ImageMagick color quantization example

jpg, q=100%, 35 Ko → png, indexed, 1.2 Ko

Download, process, stream web radios!

val src = "http://radio.hbr1.com:19800/ambient.ogg" val addEchoToOgg = CLI.pipe("sox -t ogg - -t ogg - echo 0.5 0.7 60 1") def webRadioWithEcho = Action { Ok.stream(proxy(src)(addEchoToOgg &> _)) .withHeaders(CONTENT_TYPE -> "audio/ogg") }

This works! But let's do better...

download, process and re-encode a video

val stream: Enumerator[Array[Byte]] = getStream("http://.../Sintel.2010.1080p.mkv") val scaleVideoHalf = CLI.pipe("ffmpeg -i pipe:0 -vf scale=iw/2:-1 -f avi pipe:1") val scaledVideo = stream &> scaleVideoHalf Ok.stream(scaledVideo) .withHeaders(CONTENT_TYPE -> "video/avi")

Other examples

I'm sure you have many other examples!

Important points / Limitations

Immutability / Mutability

val find = CLI.enumerate("find .")
// Re-usable - one Process each time
val consume = CLI.consume("sideEffect")
// apply this iteratee once

Termination of commands

CLI.pipe("command") is stopped if:

How to transmit the exitCode ?

Only scalable with Concurrent.broadcast

Links

SBT

"fr.greweb" %% "playcli" % "0.1"

API

http://gre.github.io/playCLI-examples/api

on Github

http://github.com/gre/playCLI-examples http://github.com/gre/playCLI

Questions?

React on

http://blog.greweb.fr/?p=2060