diff --git a/README.md b/README.md
index 2093089..f145ab1 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ Platform to manage a radio, schedules, website, and so on. We use the power of D
## Applications
* **programs**: managing stations, programs, schedules and diffusions. This is the core application, that handle most of the work;
-* **liquidsoap**: generate configuration and controls over liquidsoap. We use one instance of liquidsoap per station;
+* **controllers**: interface with external stream generators. For the moment only support [Liquidsoap](http://liquidsoap.fm/). Generate configuration files, trigger scheduled diffusions and so on;
* **cms**: cms manager with reusable tools (can be used in another website application);
* **website**: set of common models, sections, and other items ready to be used for a website;
diff --git a/liquidsoap/README.md b/liquidsoap/README.md
deleted file mode 100644
index de57c80..0000000
--- a/liquidsoap/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Aircox LiquidSoap
-This application makes the bridge between Aircox and LiquidSoap. It can monitor scheduled and streamed programs and offer some controls on LiquidSoap.
-
-
-## manage.py's commands
-* ** liquidsoap **: monitor LiquidSoap, logs what is playing on the different sources, and plays scheduled diffusions;
-* ** liquidsoap_files**: generates playlists and LiquidSoap config based on Programs' parameters;
-
-
-## Requirements
-* Liquidsoap
-* requirements.txt for python's dependencies
-
-
diff --git a/liquidsoap/__init__.py b/liquidsoap/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/liquidsoap/admin.py b/liquidsoap/admin.py
deleted file mode 100644
index ed26b1c..0000000
--- a/liquidsoap/admin.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from django.contrib import admin
-import aircox.liquidsoap.models as models
-
-@admin.register(models.Output)
-class OutputAdmin (admin.ModelAdmin):
- list_display = ('id', 'type')
-
-
diff --git a/liquidsoap/management/commands/liquidsoap.py b/liquidsoap/management/commands/liquidsoap.py
deleted file mode 100644
index 0d28203..0000000
--- a/liquidsoap/management/commands/liquidsoap.py
+++ /dev/null
@@ -1,257 +0,0 @@
-"""
-Main tool to work with liquidsoap. We can:
-- monitor Liquidsoap's sources and do logs, print what's on air.
-- generate configuration files and playlists for a given station
-"""
-import os
-import time
-import re
-import subprocess
-import atexit
-from argparse import RawTextHelpFormatter
-
-from django.conf import settings as main_settings
-from django.core.management.base import BaseCommand, CommandError
-from django.template.loader import render_to_string
-from django.utils import timezone as tz
-
-import aircox.programs.models as programs
-import aircox.programs.settings as programs_settings
-from aircox.programs.utils import to_timedelta
-
-import aircox.liquidsoap.settings as settings
-import aircox.liquidsoap.utils as utils
-
-
-class Monitor:
- @classmethod
- def run (cl, controller):
- """
- Run once the monitor on the controller
- """
- if not controller.connector.available and controller.connector.open():
- return
-
- cl.run_source(controller.master)
- cl.run_dealer(controller)
- cl.run_source(controller.dealer)
-
- for stream in controller.streams.values():
- cl.run_source(stream)
-
- @staticmethod
- def log (**kwargs):
- """
- Create a log using **kwargs, and print info
- """
- log = programs.Log(**kwargs)
- log.save()
- log.print()
-
- @classmethod
- def __get_prev_diff(cl, source, played_sounds = True):
- diff_logs = programs.Log.get_for_related_model(programs.Diffusion) \
- .filter(source = source.id) \
- .order_by('-date')
- if played_sounds:
- sound_logs = programs.Log.get_for_related_model(programs.Sound) \
- .filter(source = source.id) \
- .order_by('-date')
- if not diff_logs:
- return
-
- diff = diff_logs[0].related_object
- playlist = diff.playlist
- if played_sounds:
- diff.played = [ sound.related_object.path
- for sound in sound_logs[0:len(playlist)]
- if sound.type = program.Logs.Type.switch ]
- return diff
-
- @classmethod
- def run_dealer(cl, controller):
- # - this function must recover last state in case of crash
- # -> don't store data out of hdd
- # - construct gradually the playlist and update it if needed
- # -> we force liquidsoap to preload tracks of next diff
- # - dealer.on while last logged diff is playing, otherwise off
- # - when next diff is now and last diff no more active, play it
- # -> log and dealer.on
- dealer = controller.dealer
- now = tz.make_aware(tz.datetime.now())
- playlist = []
-
- # - the last logged diff is the last one played, it can be playing
- # -> no sound left or the diff is not more current: dealer.off
- # -> otherwise, ensure dealer.on
- # - played sounds are logged in run_source
- prev_diff = cl.__get_prev_diff(dealer)
- if prev_diff and prev_diff.is_date_in_my_range(now):
- playlist = [ path for path in prev_diff.playlist
- if path not in prev_diff.played ]
- dealer.on = bool(playlist)
- else:
- playlist = []
- dealer.on = False
-
- # - preload next diffusion's tracks
- args = {'start__gt': prev_diff.start } if prev_diff else {}
- next_diff = programs.Diffusion.get(
- now, now = True,
- type = programs.Diffusion.Type.normal,
- **args
- )
- if next_diff:
- for diff in next_diffs:
- if not diff.playlist:
- continue
- next_diff = diff
- playlist += next_diff.playlist
- break
-
- # playlist update
- if dealer.playlist != playlist:
- dealer.playlist = playlist
- if next_diff:
- cl.log(
- type = programs.Log.Type.load,
- source = dealer.id,
- date = now,
- related_object = next_diff
- )
-
- # dealer.on when next_diff.start <= now
- if next_diff and not dealer.on and next_diff.start <= now:
- dealer.on = True
- for source in controller.streams.values():
- source.skip()
- cl.log(
- type = programs.Log.Type.play,
- source = dealer.id,
- date = now,
- related_object = next_diff,
- )
-
- @classmethod
- def run_source (cl, source):
- """
- Keep trace of played sounds on the given source.
- """
- # TODO: repetition of the same sound out of an interval of time
- last_log = programs.Log.objects.filter(
- source = source.id,
- ).prefetch_related('related_object').order_by('-date')
-
- on_air = source.current_sound
- if not on_air:
- return
-
- if last_log:
- now = tz.datetime.now()
- last_log = last_log[0]
- last_obj = last_log.related_object
- if type(last_obj) == programs.Sound and on_air == last_obj.path:
- #if not last_obj.duration or \
- # now < last_log.date + to_timedelta(last_obj.duration):
- return
-
- sound = programs.Sound.objects.filter(path = on_air)
- kwargs = {
- 'type': programs.Log.Type.play,
- 'source': source.id,
- 'date': tz.make_aware(tz.datetime.now()),
- }
- if sound:
- kwargs['related_object'] = sound[0]
- else:
- kwargs['comment'] = on_air
- cl.log(**kwargs)
-
-
-class Command (BaseCommand):
- help= __doc__
- output_dir = settings.AIRCOX_LIQUIDSOAP_MEDIA
-
- def add_arguments (self, parser):
- parser.formatter_class=RawTextHelpFormatter
- parser.add_argument(
- '-e', '--exec', action='store_true',
- help='run liquidsoap on exit'
- )
- group.add_argument(
- '-s', '--station', type=str,
- default = 'aircox',
- help='use this name as station name (default is "aircox")'
- )
-
- group = parser.add_argument_group('actions')
- group.add_argument(
- '-d', '--delay', type=int,
- default=1000,
- help='time to sleep in milliseconds between two updates when we '
- 'monitor'
- )
- group.add_argument(
- '-m', '--monitor', action='store_true',
- help='run in monitor mode'
- )
- group.add_argument(
- '-o', '--on_air', action='store_true',
- help='print what is on air'
- )
- group.add_argument(
- '-r', '--run', action='store_true',
- help='run liquidsoap with the generated configuration'
- )
- group.add_argument(
- '-w', '--write', action='store_true',
- help='write configuration and playlist'
- )
-
- def handle (self, *args, **options):
- run = options.get('run')
- monitor = options.get('on_air') or options.get('monitor')
- self.controller = utils.Controller(
- station = options.get('station'),
- connector = monitor
- )
-
- # actions
- if options.get('write') or run:
- self.handle_write()
- if run:
- self.handle_run()
- if monitor:
- self.handle_monitor(options)
-
- # post
- if run:
- for controller in self.controllers:
- controller.process.wait()
-
- def handle_write (self):
- self.controller.write()
-
- def handle_run (self):
- self.controller.process = \
- subprocess.Popen(
- ['liquidsoap', '-v', self.controller.config_path],
- stderr=subprocess.STDOUT
- )
- atexit.register(self.controller.process.terminate)
-
- def handle_monitor (self, options):
- self.controller.update()
-
- if options.get('on_air'):
- print(self.controller.id, self.controller.on_air)
- return
-
- if options.get('monitor'):
- delay = options.get('delay') / 1000
- while True:
- Monitor.run(self.controller)
- time.sleep(delay)
- return
-
-
diff --git a/liquidsoap/models.py b/liquidsoap/models.py
deleted file mode 100644
index 6764c84..0000000
--- a/liquidsoap/models.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from enum import Enum, IntEnum
-
-from django.db import models
-from django.utils.translation import ugettext as _, ugettext_lazy
-
-
-class Output (models.Model):
- # Note: we don't translate the names since it is project names.
- class Type(IntEnum):
- jack = 0x00
- alsa = 0x01
- icecast = 0x02
-
- type = models.SmallIntegerField(
- _('output type'),
- choices = [ (int(y), _(x)) for x,y in Type.__members__.items() ],
- blank = True, null = True
- )
- settings = models.TextField(
- _('output settings'),
- help_text = _('list of comma separated params available; '
- 'this is put in the output config as raw code'),
- blank = True, null = True
- )
-
-
diff --git a/liquidsoap/settings.py b/liquidsoap/settings.py
deleted file mode 100644
index 161e90f..0000000
--- a/liquidsoap/settings.py
+++ /dev/null
@@ -1,23 +0,0 @@
-
-from django.conf import settings
-
-def ensure (key, default):
- globals()[key] = getattr(settings, key, default)
-
-
-# dict of values to set (do not forget to escape chars)
-ensure('AIRCOX_LIQUIDSOAP_SET', {
- 'log.file.path': '"/tmp/liquidsoap.log"',
-})
-
-# security source: used when no source are available
-ensure('AIRCOX_LIQUIDSOAP_SECURITY_SOURCE', '/media/data/musique/creation/Mega Combi/MegaCombi241-PT134-24062015_Comme_des_lyca_ens.mp3')
-
-# start the server on monitor if not present
-ensure('AIRCOX_LIQUIDSOAP_AUTOSTART', True)
-
-# output directory for the generated files and socket. Each station has a subdir
-# with the station's slug as name.
-ensure('AIRCOX_LIQUIDSOAP_MEDIA', '/tmp')
-
-
diff --git a/liquidsoap/templates/aircox/liquidsoap/controller.html b/liquidsoap/templates/aircox/liquidsoap/controller.html
deleted file mode 100644
index 518305e..0000000
--- a/liquidsoap/templates/aircox/liquidsoap/controller.html
+++ /dev/null
@@ -1,134 +0,0 @@
-{% if not embed %}
-
-
-
-
-
-
-
-
-{% endif %}
- {% for c_id, controller in monitor.controllers.items %}
- {% with on_air=controller.on_air %}
-
-
- {% if not controller.connector.available %}
- disconnected
- {% endif %}
-
- {{ controller.station.name }}
-
-
-
- {% with source=controller.master %}
- {% include 'aircox/liquidsoap/source.html' %}
- {% endwith %}
-
- {% with source=controller.dealer %}
- {% include 'aircox/liquidsoap/source.html' %}
- {% endwith %}
-
- {% for source in controller.streams.values %}
- {% include 'aircox/liquidsoap/source.html' %}
- {% endfor %}
-
-
- {% for diffusion in controller.next_diffusions %}
- {{ diffusion }}
- {% endfor %}
-