From bd987bd62c5c219933ef91d753887a7da2b46599 Mon Sep 17 00:00:00 2001 From: bkfox Date: Tue, 3 Nov 2015 23:20:03 +0100 Subject: [PATCH] start liquidsoap part; fix some bugs; update Stream model --- .../management/commands/liquidsoap_files.py | 125 ++++++++++++++++++ aircox_liquidsoap/settings.py | 24 ++++ aircox_programs/admin.py | 2 +- .../management/commands/sounds_monitor.py | 1 - aircox_programs/models.py | 13 +- 5 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 aircox_liquidsoap/management/commands/liquidsoap_files.py create mode 100644 aircox_liquidsoap/settings.py diff --git a/aircox_liquidsoap/management/commands/liquidsoap_files.py b/aircox_liquidsoap/management/commands/liquidsoap_files.py new file mode 100644 index 0000000..c3f72a8 --- /dev/null +++ b/aircox_liquidsoap/management/commands/liquidsoap_files.py @@ -0,0 +1,125 @@ +""" +Generate configuration files and playlists for liquidsoap using settings, streams and +so on +""" +import os +import re +from argparse import RawTextHelpFormatter + +from django.core.management.base import BaseCommand, CommandError +from django.views.generic.base import View +from django.template.loader import render_to_string + +import aircox_liquidsoap.settings as settings +import aircox_programs.models as models + + + +class Command (BaseCommand): + help= __doc__ + output_dir = settings.AIRCOX_LIQUIDSOAP_MEDIA + + def add_arguments (self, parser): + parser.formatter_class=RawTextHelpFormatter + parser.add_argument( + 'output', metavar='PATH', type=str, nargs='?', + help='force output to file (- to stdout) for single actions; to a ' + 'given dir when using --all') + parser.add_argument( + '-c', '--config', action='store_true', + help='Generate liquidsoap config file' + ) + parser.add_argument( + '-s', '--stream', type=int, + help='Generate the playlist of a stream with the given id' + ) + parser.add_argument( + '-S', '--streams', action='store_true', + help='Generate all playlists' + ) + parser.add_argument( + '-a', '--all', action='store_true', + help='Generate all playlists and config file' + ) + + def handle (self, *args, **options): + output = options.get('output') or None + if options.get('config'): + data = self.get_config(output = output) + return + + if options.get('stream'): + stream = options['stream'] + if type(stream) is int: + stream = models.Stream.objects.get(id = stream) + + data = self.get_playlist(stream, output = output) + return + + if options.get('all') or options.get('streams'): + if output: + if not os.path.isdir(output): + raise CommandError('given output is not a directory') + self.output_dir = output + + if options.get('all'): + self.handle(config = True) + + for stream in models.Stream.objects.filter(active = True): + self.handle(stream = stream) + self.output_dir = settings.AIRCOX_LIQUIDSOAP_MEDIA + return + + raise CommandError('nothing to do') + + def print (self, data, path, default): + if path and path == '-': + print(data) + return + + if not path: + path = os.path.join(self.output_dir, default) + with open(path, 'w+') as file: + file.write(data) + + @staticmethod + def __render_stream_in_radio (stream): + if stream.time_start and stream.time_end: + data = '({}-{}, {})'.format( + stream.time_start.strftime('%Hh%M'), + stream.time_end.strftime('%Hh%M'), + stream.get_slug_name() + ) + else: + data = stream.get_slug_name() + + if stream.delay: + data = 'delay({}., {})'.format( + stream.delay.strftime('%s'), + data + ) + return data + + def get_config (self, output = None): + streams = models.Stream.objects.filter(active = True).order_by('type')[:] + for stream in streams: + stream.render_in_radio = self.__render_stream_in_radio(stream) + + context = { + 'streams': streams, + 'settings': settings, + } + + data = render_to_string('aircox_liquidsoap/config.liq', context) + data = re.sub(r'\\\n', r'#\\n#', data) + data = data.replace('\n', '') + data = re.sub(r'#\\n#', '\n', data) + self.print(data, output, 'aircox.liq') + + def get_playlist (self, stream, output = None): + data = '/media/data/musique/free/Professor Kliq -- 28 Days With The OP-1' \ + '-- jm148689/1_Coffee.ogg\n' + self.print(data, output, 'stream_{}.m3u'.format(stream.pk)) + + + diff --git a/aircox_liquidsoap/settings.py b/aircox_liquidsoap/settings.py new file mode 100644 index 0000000..0912e3f --- /dev/null +++ b/aircox_liquidsoap/settings.py @@ -0,0 +1,24 @@ + +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_CONFIG', { + 'log.file.path': '"/tmp/liquidsoap.log"', + 'server.socket': 'true', + 'server.socket.path': '"/tmp/liquidsoap.sock"' +}) + +# 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 +ensure('AIRCOX_LIQUIDSOAP_MEDIA', '/tmp') + + diff --git a/aircox_programs/admin.py b/aircox_programs/admin.py index 16e0422..06605df 100755 --- a/aircox_programs/admin.py +++ b/aircox_programs/admin.py @@ -60,7 +60,7 @@ class SoundAdmin (NameableAdmin): @admin.register(Stream) class StreamAdmin (SortableModelAdmin): - list_display = ('id', 'name', 'type', 'priority') + list_display = ('id', 'name', 'type') sortable = "priority" diff --git a/aircox_programs/management/commands/sounds_monitor.py b/aircox_programs/management/commands/sounds_monitor.py index e31132d..2cab3db 100644 --- a/aircox_programs/management/commands/sounds_monitor.py +++ b/aircox_programs/management/commands/sounds_monitor.py @@ -102,7 +102,6 @@ class Command (BaseCommand): self.report(program, path, 'no diffusion found for the given date') return diffusion = diffusion[0] - print(diffusion, sound_info) return diffusion.episode or None @staticmethod diff --git a/aircox_programs/models.py b/aircox_programs/models.py index 4316152..ee0f966 100755 --- a/aircox_programs/models.py +++ b/aircox_programs/models.py @@ -404,14 +404,19 @@ class Stream (Nameable): default = True, help_text = _('program list is public'), ) + active = models.BooleanField( + _('active'), + default = True, + help_text = _('stream is active') + ) type = models.SmallIntegerField( verbose_name = _('type'), choices = [ (y, x) for x,y in Type.items() ], ) - priority = models.SmallIntegerField( - _('priority'), - default = 0, - help_text = _('priority of the stream') + delay = models.TimeField( + _('delay'), + blank = True, null = True, + help_text = _('play this playlist at least every delay') ) time_start = models.TimeField( _('start'),