Allow more than one parent for playable objects

This turns the playlist tree into a playlist graph.

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>
Don't set next track until play is called

This allows the playlist to be populated after the player
is initialized.

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>
Fix __str__ for playlists

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>
Only poll the gstreamer bus if there is something on it

gst.bus.poll() sometimes hang for some reason, this seems
to be a work around it.

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>
Only poll for the gstreamer messages we need

Reduce the amount of callbacks from the bus by only polling
for EOS (end-of-stream) and ERROR.

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>
Better handling of empty/non-playable playlists

Let playlist.next() return None if the playlist is empty and
don't attempt to play if no file could be selected.

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>
Add dynamic and controllable trigger support

This moves control of the existing triggers, GPIO and signal, to
the playlist and makes them configurable.  Triggers are assigned per
sub-playlist or in the DEFAULTS scope.  Multiple triggers are allowed
per playlist, but at least one is required (for the playlist itself or
in any of its parents) for the playlist to become active.

The syntax to define a trigger is
 trigger=<player_id> <type> <arguments>

The player id is a user defined string the identifies which player this
trigger belongs to.  Multiple triggers can be mapped to the same player,
this allows one player to be controlled by multiple triggers.

To configure a GPIO trigger
 trigger=default gpio path=/path/to/gpio/pin

And to configure a signal trigger
 trigger=default singal toggle=<signum> [next=<signum>]
where signum can be sigusr1, sigusr2

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>
Add a random trigger

This trigger will randomly play a sound with configurable probability.

 trigger=<player> random delay=<seconds> dice=<roll>/<eyes>

Delay specifies how often the "dice" should be rolled.
Dice have two components, eyes are the total amount of eyes on the dice,
and roll specifies what counts as a win.  Everything upto and below
for <roll> counts as a win.

Examples

Will roll the dice once a second with a 1/6 probability of playing
 trigger=randomplayer delay=1 dice=1/6

Will roll the dice every 5 minutes with a 5/10 probability of playing
 trigger=randomplayer delay=300 dice=5/10

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>
fix trigger_gpio

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>
Isolate each gstreamer instance to a separate process

Move each (relative) costly gstreamer player into separate processes,
since Python threads share the same execution context.  This allows
the gstream thread to run freely without the need to share execution
with the rest of the control code.  Control and state feedback
of each instance is performed over a pipe.

This also removes the need of a global watchdog as each gstreamer
instance will be monitored and restarted as needed.

Signed-off-by: Fredrik Lindberg <fli@shapeshifter.se>