From 2445690da3af777f920e8a5fae112e9ca3eedcd9 Mon Sep 17 00:00:00 2001 From: bkfox Date: Wed, 23 Dec 2015 12:40:57 +0100 Subject: [PATCH] roll back to older logging method (we don't wan't to run logging on liquidsoap's main thread. Move monitoring in appropriate file --- liquidsoap/management/commands/liquidsoap.py | 147 ++++++++++++++++-- .../templates/aircox/liquidsoap/station.liq | 16 +- liquidsoap/utils.py | 81 +--------- 3 files changed, 145 insertions(+), 99 deletions(-) diff --git a/liquidsoap/management/commands/liquidsoap.py b/liquidsoap/management/commands/liquidsoap.py index c53e86f..d384f45 100644 --- a/liquidsoap/management/commands/liquidsoap.py +++ b/liquidsoap/management/commands/liquidsoap.py @@ -13,8 +13,9 @@ 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 models +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 @@ -61,9 +62,9 @@ class StationConfig: for stream in self.controller.streams.values(): program = stream.program - sounds = models.Sound.objects.filter( + sounds = programs.Sound.objects.filter( # good_quality = True, - type = models.Sound.Type['archive'], + type = programs.Sound.Type['archive'], path__startswith = os.path.join( programs_settings.AIRCOX_SOUND_ARCHIVES_SUBDIR, program.path @@ -73,6 +74,120 @@ class StationConfig: file.write('\n'.join(sound.path for sound in sounds)) +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) + + 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() + + @staticmethod + def expected_diffusion (station, date, on_air): + """ + Return which diffusion should be played now and is not playing + on the given station + """ + r = [ programs.Diffusion.get_prev(station, date), + programs.Diffusion.get_next(station, date) ] + r = [ diffusion.prefetch_related('sounds')[0] + for diffusion in r if diffusion.count() ] + + for diffusion in r: + duration = to_timedelta(diffusion.archives_duration()) + end_at = diffusion.date + duration + if end_at < date: + continue + + diffusion.playlist = [ sound.path + for sound in diffusion.get_archives() ] + if diffusion.playlist and on_air not in diffusion.playlist: + return diffusion + + @classmethod + def run_dealer (cl, controller): + """ + Monitor dealer playlist (if it is time to load) and whether it is time + to trigger the button to start a diffusion. + """ + dealer = controller.dealer + playlist = dealer.playlist + on_air = dealer.current_sound + now = tz.make_aware(tz.datetime.now()) + + diff = cl.expected_diffusion(controller.station, now, on_air) + if not diff: + return # there is nothing we can do + + # playlist reload + if dealer.playlist != diff.playlist: + if not playlist or on_air == playlist[-1] or \ + on_air not in playlist: + dealer.on = False + dealer.playlist = diff.playlist + + # run the diff + if dealer.playlist == diff.playlist and diff.date <= now and not dealer.on: + dealer.on = True + for source in controller.streams.values(): + source.skip() + cl.log( + source = dealer.id, + date = now, + comment = 'trigger the scheduled diffusion to liquidsoap; ' + 'skip all other streams', + related_object = diff, + ) + + @classmethod + def run_source (cl, source): + """ + Keep trace of played sounds on the given source. For the moment we only + keep track of known sounds. + """ + 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: + last_log = last_log[0] + if type(last_log.related_object) == programs.Sound and \ + on_air == last_log.related_object.path: + return + + sound = programs.Sound.objects.filter(path = on_air) + if not sound: + return + + sound = sound[0] + cl.log( + source = source.id, + date = tz.make_aware(tz.datetime.now()), + comment = 'sound has changed', + related_object = sound or None, + ) + + class Command (BaseCommand): help= __doc__ output_dir = settings.AIRCOX_LIQUIDSOAP_MEDIA @@ -117,33 +232,37 @@ class Command (BaseCommand): def handle (self, *args, **options): if options.get('station'): - station = models.Station.objects.get(id = options.get('station')) + station = programs.Station.objects.get(id = options.get('station')) StationConfig(station).handle(options) elif options.get('all') or options.get('config') or \ options.get('streams'): - for station in models.Station.objects.filter(active = True): + for station in programs.Station.objects.filter(active = True): StationConfig(station).handle(options) if options.get('on_air') or options.get('monitor'): self.handle_monitor(options) def handle_monitor (self, options): - self.monitor = utils.Monitor() - self.monitor.update() + controllers = [ + utils.Controller(station) + for station in programs.Station.objects.filter(active = True) + ] + for controller in controllers: + controller.update() if options.get('on_air'): - for id, controller in self.monitor.controller.items(): - print(id, controller.on_air) + for controller in controllers: + print(controller.id, controller.on_air) return if options.get('monitor'): delay = options.get('delay') / 1000 while True: - for controller in self.monitor.controllers.values(): - try: - controller.monitor() - except Exception as err: - print(err) + for controller in controllers: + #try: + Monitor.run(controller) + #except Exception as err: + # print(err) time.sleep(delay) return diff --git a/liquidsoap/templates/aircox/liquidsoap/station.liq b/liquidsoap/templates/aircox/liquidsoap/station.liq index 3f52214..820ae4d 100644 --- a/liquidsoap/templates/aircox/liquidsoap/station.liq +++ b/liquidsoap/templates/aircox/liquidsoap/station.liq @@ -2,21 +2,15 @@ {# - controller: controller used to generate the current file #} {# - settings: global settings #} def interactive_source (id, s) = \ - def handler(m) = \ - file = string.escape(m['filename']) \ - system('echo {{ log_script }} -s "#{id}" -p \"#{file}\" -c "liquidsoap: play" &') \ - system('{{ log_script }} -s "#{id}" -p \"#{file}\" -c "liquidsoap: play" &') \ - end \ - \ - s = on_track(id=id, handler, s) - # s = store_metadata(id=id, size=1, s) \ + s = store_metadata(id=id, size=1, s) \ add_skip_command(s) \ s \ end \ \ def stream (id, file) = \ - s = playlist(id = '#{id}_playlist', mode = "random", \ - reload_mode='watch', file) \ + #s = playlist(id = '#{id}_playlist', mode = "random", \ + # file) \ + s = playlist(id = '#{id}_playlist', mode = "random", file) interactive_source(id, s) \ end \ \ @@ -66,7 +60,7 @@ set("{{ key|safe }}", {{ value|safe }}) \ {% if controller.station.fallback %} single("{{ controller.station.fallback }}"), \ {% else %} - blank(duration=0.1), \ + blank(id="scheize_blank", duration=0.1), \ {% endif %} ]) \ ) \ diff --git a/liquidsoap/utils.py b/liquidsoap/utils.py index 00b5ebf..b00745c 100644 --- a/liquidsoap/utils.py +++ b/liquidsoap/utils.py @@ -6,9 +6,7 @@ import json from django.utils.translation import ugettext as _, ugettext_lazy from django.utils import timezone as tz -from aircox.programs.utils import to_timedelta import aircox.programs.models as programs - import aircox.liquidsoap.models as models import aircox.liquidsoap.settings as settings @@ -57,7 +55,7 @@ class Connector: self.__socket.sendall(data) data = '' while not reg.search(data): - data += self.__socket.recv(1024).decode('unicode_escape') + data += self.__socket.recv(1024).decode('utf-8') if data: data = reg.sub(r'\1', data) @@ -259,59 +257,6 @@ class Dealer (Source): return self.connector.send('var.set ', self.id, '_on', '=', 'true' if value else 'false') - def __get_next (self, date, on_air): - """ - Return which diffusion should be played now and is not playing - """ - r = [ programs.Diffusion.get_prev(self.station, date), - programs.Diffusion.get_next(self.station, date) ] - r = [ diffusion.prefetch_related('sounds')[0] - for diffusion in r if diffusion.count() ] - - for diffusion in r: - duration = to_timedelta(diffusion.archives_duration()) - end_at = diffusion.date + duration - if end_at < date: - continue - - diffusion.playlist = [ sound.path - for sound in diffusion.get_archives() ] - if diffusion.playlist and on_air not in diffusion.playlist: - return diffusion - - def monitor (self): - """ - Monitor playlist (if it is time to load) and if it time to trigger - the button to start a diffusion. - """ - playlist = self.playlist - on_air = self.current_sound - now = tz.make_aware(tz.datetime.now()) - - diff = self.__get_next(now, on_air) - if not diff: - return # there is nothing we can do - - # playlist reload - if self.playlist != diff.playlist: - if not playlist or on_air == playlist[-1] or \ - on_air not in playlist: - self.on = False - self.playlist = diff.playlist - - # run the diff - if self.playlist == diff.playlist and diff.date <= now and not self.on: - self.on = True - for source in self.controller.streams.values(): - source.skip() - self.controller.log( - source = self.id, - date = now, - comment = 'trigger the scheduled diffusion to liquidsoap; ' - 'skip all other streams', - related_object = diff, - ) - class Controller: """ Main class controller for station and sources (streams and dealer) @@ -356,7 +301,7 @@ class Controller: """ return os.path.join(self.path, 'station.liq') - def __init__ (self, station, connector = True): + def __init__ (self, station, connector = True, update = False): """ Params: - station: managed station @@ -384,6 +329,9 @@ class Controller: ] } + if update: + self.update() + def get (self, source_id): """ Get a source by its id @@ -394,15 +342,8 @@ class Controller: return self.dealer return self.streams.get(source_id) - def log (self, **kwargs): - """ - Create a log using **kwargs, and print info - """ - log = programs.Log(**kwargs) - log.save() - log.print() - def update_all (self): + def update (self): """ Fetch and update all streams metadata. """ @@ -411,14 +352,6 @@ class Controller: for source in self.streams.values(): source.update() - def monitor (self): - """ - Log changes in the streams, and call dealer.monitor. - """ - if not self.connector.available and self.connector.open(): - return - self.dealer.monitor() - class Monitor: """ @@ -437,6 +370,6 @@ class Monitor: def update (self): for controller in self.controllers.values(): - controller.update_all() + controller.update()