redesign streams, make liquidsoap working with timed streams

This commit is contained in:
bkfox 2015-11-05 16:59:28 +01:00
parent bd987bd62c
commit 758bcb30a2
4 changed files with 73 additions and 87 deletions

View File

@ -11,6 +11,7 @@ from django.views.generic.base import View
from django.template.loader import render_to_string
import aircox_liquidsoap.settings as settings
import aircox_programs.settings as programs_settings
import aircox_programs.models as models
@ -51,7 +52,8 @@ class Command (BaseCommand):
if options.get('stream'):
stream = options['stream']
if type(stream) is int:
stream = models.Stream.objects.get(id = stream)
stream = models.Stream.objects.get(id = stream,
program__active = True)
data = self.get_playlist(stream, output = output)
return
@ -65,7 +67,7 @@ class Command (BaseCommand):
if options.get('all'):
self.handle(config = True)
for stream in models.Stream.objects.filter(active = True):
for stream in models.Stream.objects.filter(program__active = True):
self.handle(stream = stream)
self.output_dir = settings.AIRCOX_LIQUIDSOAP_MEDIA
return
@ -85,13 +87,13 @@ class Command (BaseCommand):
@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()
data = '({{{}h-{}h}}, {})'.format(
stream.time_start.hour,
stream.time_end.hour,
stream.program.get_slug_name()
)
else:
data = stream.get_slug_name()
data = stream.program.get_slug_name()
if stream.delay:
data = 'delay({}., {})'.format(
@ -101,7 +103,7 @@ class Command (BaseCommand):
return data
def get_config (self, output = None):
streams = models.Stream.objects.filter(active = True).order_by('type')[:]
streams = models.Stream.objects.filter(program__active = True)
for stream in streams:
stream.render_in_radio = self.__render_stream_in_radio(stream)
@ -111,15 +113,22 @@ class Command (BaseCommand):
}
data = render_to_string('aircox_liquidsoap/config.liq', context)
data = re.sub(r'\\\n', r'#\\n#', data)
data = re.sub(r'\s*\\\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'
path = os.path.join(
programs_settings.AIRCOX_SOUND_ARCHIVES_SUBDIR,
stream.program.path
)
sounds = models.Sound.objects.filter(
# good_quality = True,
type = models.Sound.Type['archive'],
path__startswith = path
)
data = '\n'.join(sound.path for sound in sounds)
self.print(data, output, 'stream_{}.m3u'.format(stream.pk))

View File

@ -26,8 +26,8 @@ class ScheduleInline (admin.TabularInline):
class DiffusionInline (admin.TabularInline):
model = Diffusion
fields = ('episode', 'type', 'date', 'stream')
readonly_fields = ('date', 'stream')
fields = ('episode', 'type', 'date')
readonly_fields = ('date',)
extra = 1
@ -59,14 +59,13 @@ class SoundAdmin (NameableAdmin):
@admin.register(Stream)
class StreamAdmin (SortableModelAdmin):
list_display = ('id', 'name', 'type')
sortable = "priority"
class StreamAdmin (admin.ModelAdmin):
list_display = ('id', 'program', 'delay', 'time_start', 'time_end')
@admin.register(Program)
class ProgramAdmin (NameableAdmin):
fields = NameableAdmin.fields + ['stream']
fields = NameableAdmin.fields
inlines = [ ScheduleInline ]
@ -86,8 +85,8 @@ class DiffusionAdmin (admin.ModelAdmin):
if sound.type == Sound.Type['archive'] )
return ', '.join(sounds) if sounds else ''
list_display = ('id', 'type', 'date', 'archives', 'episode', 'program', 'stream')
list_filter = ('type', 'date', 'program', 'stream')
list_display = ('id', 'type', 'date', 'archives', 'episode', 'program')
list_filter = ('type', 'date', 'program')
list_editable = ('type', 'date')
def get_queryset(self, request):

View File

@ -59,22 +59,24 @@ class Command (BaseCommand):
if options.get('quality_check'):
self.check_quality(check = (not options.get('scan')) )
def get_sound_info (self, path):
def get_sound_info (self, program, path):
"""
Parse file name to get info on the assumption it has the correct
format (given in Command.help)
"""
file_name = os.path.basename(path)
file_name = os.path.splitext(file_name)[0]
r = re.search('^(?P<year>[0-9]{4})'
'(?P<month>[0-9]{2})'
'(?P<day>[0-9]{2})'
'(_(?P<n>[0-9]+))?'
'_?(?P<name>.*)\.\w+$',
os.path.basename(path))
'_?(?P<name>.*)$',
file_name)
if not (r and r.groupdict()):
self.report(program, path, "file path is not correct, use defaults")
r = {
'name': os.path.splitext(path)[0]
'name': file_name
}
else:
r = r.groupdict()
@ -143,7 +145,7 @@ class Command (BaseCommand):
if not path.endswith(settings.AIRCOX_SOUND_FILE_EXT):
continue
sound_info = self.get_sound_info(path)
sound_info = self.get_sound_info(program, path)
sound = Sound.objects.get_or_create(
path = path,
defaults = { 'name': sound_info['name'] }

View File

@ -31,7 +31,7 @@ class Nameable (models.Model):
)
def get_slug_name (self):
return slugify(self.name)
return slugify(self.name).replace('-', '_')
def __str__ (self):
#if self.pk:
@ -137,6 +137,8 @@ class Sound (Nameable):
"""
mtime = os.stat(self.path).st_mtime
mtime = tz.datetime.fromtimestamp(mtime)
# db does not store microseconds
mtime = mtime.replace(microsecond = 0)
return tz.make_aware(mtime, tz.get_current_timezone())
def file_exists (self):
@ -181,6 +183,38 @@ class Sound (Nameable):
verbose_name_plural = _('Sounds')
class Stream (models.Model):
"""
When there are no program scheduled, it is possible to play sounds
in order to avoid blanks. A Stream is a Program that plays this role,
and whose linked to a Stream.
All sounds that are marked as good and that are under the related
program's archive dir are elligible for the sound's selection.
"""
program = models.ForeignKey(
'Program',
verbose_name = _('related program'),
)
delay = models.TimeField(
_('delay'),
blank = True, null = True,
help_text = _('plays this playlist at least every delay')
)
time_start = models.TimeField(
_('start'),
blank = True, null = True,
help_text = _('used to define a time range this stream is'
'played')
)
time_end = models.TimeField(
_('end'),
blank = True, null = True,
help_text = _('used to define a time range this stream is'
'played')
)
class Schedule (models.Model):
# Frequency for schedules. Basically, it is a mask of bits where each bit is
# a week. Bits > rank 5 are used for special schedules.
@ -203,7 +237,7 @@ class Schedule (models.Model):
program = models.ForeignKey(
'Program',
blank = True, null = True,
verbose_name = _('related program'),
)
date = models.DateTimeField(_('date'))
duration = models.TimeField(
@ -327,7 +361,6 @@ class Schedule (models.Model):
diffusions.append(Diffusion(
episode = episode,
program = self.program,
stream = self.program.stream,
type = Diffusion.Type['unconfirmed'],
date = date,
))
@ -363,13 +396,6 @@ class Diffusion (models.Model):
'Program',
verbose_name = _('program'),
)
# program.stream can change, but not the stream;
stream = models.ForeignKey(
'Stream',
verbose_name = _('stream'),
default = 0,
help_text = 'stream id on which the diffusion happens',
)
type = models.SmallIntegerField(
verbose_name = _('type'),
choices = [ (y, x) for x,y in Type.items() ],
@ -379,7 +405,6 @@ class Diffusion (models.Model):
def save (self, *args, **kwargs):
if self.episode: # FIXME self.episode or kwargs['episode']
self.program = self.episode.program
# check type against stream's type
super(Diffusion, self).save(*args, **kwargs)
def __str__ (self):
@ -391,56 +416,7 @@ class Diffusion (models.Model):
verbose_name_plural = _('Diffusions')
class Stream (Nameable):
Type = {
'random': 0x00, # selection using random function
'schedule': 0x01, # selection using schedule
}
for key, value in Type.items():
ugettext_lazy(key)
public = models.BooleanField(
_('public'),
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() ],
)
delay = models.TimeField(
_('delay'),
blank = True, null = True,
help_text = _('play this playlist at least every delay')
)
time_start = models.TimeField(
_('start'),
blank = True, null = True,
help_text = _('if random, used to define a time range this stream is'
'played')
)
time_end = models.TimeField(
_('end'),
blank = True, null = True,
help_text = _('if random, used to define a time range this stream is'
'played')
)
# get info for:
# - random lists
# - scheduled lists
class Program (Nameable):
stream = models.ForeignKey(
Stream,
verbose_name = _('streams'),
)
active = models.BooleanField(
_('inactive'),
default = True,
@ -450,7 +426,7 @@ class Program (Nameable):
@property
def path (self):
return os.path.join(settings.AIRCOX_PROGRAMS_DIR,
slugify(self.name + '_' + str(self.id)) )
self.get_slug_name() + '_' + str(self.id) )
def ensure_dir (self, subdir = None):
"""
@ -477,12 +453,12 @@ class Program (Nameable):
if schedule.match(date, check_time = False):
return schedule
class Episode (Nameable):
program = models.ForeignKey(
Program,
verbose_name = _('program'),
help_text = _('parent program'),
blank = True, null = True,
)
sounds = models.ManyToManyField(
Sound,