fix error in streamer and on_air

This commit is contained in:
bkfox 2017-07-02 18:09:40 +02:00
parent 013a0894ab
commit 08cb8e71bb
5 changed files with 108 additions and 52 deletions

View File

@ -54,10 +54,18 @@ class Monitor:
Datetime of the next sync Datetime of the next sync
""" """
_last_log = None
"""
Last emitted log
"""
def __init__(self, station, **kwargs): def __init__(self, station, **kwargs):
self.station = station self.station = station
self.__dict__.update(kwargs) self.__dict__.update(kwargs)
self._last_log = Log.objects.station(station).order_by('date') \
.last()
def monitor(self): def monitor(self):
""" """
Run all monitoring functions. Run all monitoring functions.
@ -81,6 +89,12 @@ class Monitor:
log.save() log.save()
log.print() log.print()
# update last log
if log.type != Log.Type.other and \
self._last_log and not self._last_log.end:
self._last_log.end = log.date
self._last_log = log
def trace(self): def trace(self):
""" """
Check the current_sound of the station and update logs if Check the current_sound of the station and update logs if
@ -106,12 +120,12 @@ class Monitor:
log.sound.path == current_sound): log.sound.path == current_sound):
return return
sound = Sound.objects.filter(path = current_sound) sound = Sound.objects.filter(path = current_sound).first()
self.log( self.log(
type = Log.Type.play, type = Log.Type.on_air,
source = current_source.id, source = current_source.id,
date = tz.now(), date = tz.now(),
sound = sound[0] if sound else None, sound = sound,
# keep sound path (if sound is removed, we keep that info) # keep sound path (if sound is removed, we keep that info)
comment = current_sound, comment = current_sound,
) )
@ -135,14 +149,13 @@ class Monitor:
now = tz.now() now = tz.now()
for track in tracks: for track in tracks:
pos = log.date + tz.timedelta(seconds = track.position) pos = log.date + tz.timedelta(seconds = track.position)
if pos < now: if pos > now:
self.log( break
type = Log.Type.play, self.log(
source = log.source, type = Log.Type.on_air, source = log.source,
date = pos, date = pos, track = track,
track = track, comment = track,
comment = track, )
)
def sync_playlists(self): def sync_playlists(self):
""" """
@ -243,11 +256,16 @@ class Monitor:
it is needed. it is needed.
""" """
dealer = self.station.dealer dealer = self.station.dealer
if dealer.playlist != playlist: if dealer.playlist == playlist:
dealer.playlist = playlist return
if diff and not diff.is_live():
self.log(type = Log.Type.load, source = source.id, dealer.playlist = playlist
diffusion = diff, date = date) if diff and not diff.is_live():
self.log(type = Log.Type.load,
source = source.id,
diffusion = diff,
date = date,
comment = '\n'.join(playlist))
def handle_diff_start(self, source, diff, date): def handle_diff_start(self, source, diff, date):
""" """
@ -257,18 +275,22 @@ class Monitor:
if not diff or diff.start > date: if not diff or diff.start > date:
return return
# TODO: user has not yet put the diffusion sound when diff started
# => live logged; what we want: if user put a sound after it
# has been logged as live, load and start this sound
# live: just log it # live: just log it
if diff.is_live(): if diff.is_live():
diff_ = Log.objects.station(self.station) \ diff_ = Log.objects.station(self.station) \
.filter(diffusion = diff) .filter(diffusion = diff, type = Log.Type.on_air)
if not diff_.count(): if not diff_.count():
self.log(type = Log.Type.on_air, source = source.id, self.log(type = Log.Type.on_air, source = source.id,
diffusion = diff, date = date) diffusion = diff, date = date)
return return
# enable dealer # enable dealer
if not dealer.active: if not source.active:
dealer.active = True source.active = True
self.log(type = Log.Type.play, source = source.id, self.log(type = Log.Type.play, source = source.id,
diffusion = diff, date = date) diffusion = diff, date = date)

View File

@ -278,7 +278,7 @@ class Station(Nameable):
for diff in diffs: for diff in diffs:
if count and n >= count: if count and n >= count:
break break
q = q | models.Q(date__gte = diff.start, date__lte = diff.end) q = q | models.Q(date__gte = diff.start, end__lte = diff.end)
n += 1 n += 1
logs = logs.exclude(q, diffusion__isnull = True) logs = logs.exclude(q, diffusion__isnull = True)
@ -1197,6 +1197,8 @@ class LogManager(models.Manager):
def _at(self, date = None, qs = None, **kwargs): def _at(self, date = None, qs = None, **kwargs):
start, end = utils.date_range(date) start, end = utils.date_range(date)
qs = self if qs is None else qs qs = self if qs is None else qs
# return qs.filter(models.Q(end__gte = start) |
# models.Q(date__lte = end))
return qs.filter(date__gte = start, date__lte = end, **kwargs) return qs.filter(date__gte = start, date__lte = end, **kwargs)
def at(self, station = None, date = None, qs = None, **kwargs): def at(self, station = None, date = None, qs = None, **kwargs):
@ -1207,8 +1209,7 @@ class LogManager(models.Manager):
qs = self._at(date, qs, **kwargs) qs = self._at(date, qs, **kwargs)
return self.station(station, qs) if station else qs return self.station(station, qs) if station else qs
def played(self, station, archives = True, include_live = True, def played(self, station, archives = True, **kwargs):
**kwargs):
""" """
Return a queryset of the played elements' log for the given Return a queryset of the played elements' log for the given
station and model. This queryset is ordered by date ascending station and model. This queryset is ordered by date ascending
@ -1219,11 +1220,8 @@ class LogManager(models.Manager):
* include_live: include diffusion that have no archive * include_live: include diffusion that have no archive
* kwargs: extra filter kwargs * kwargs: extra filter kwargs
""" """
if include_live: qs = self.filter(type__in = (Log.Type.play, Log.Type.on_air),
qs = self.filter(type__in = (Log.Type.play, Log.Type.on_air), **kwargs)
**kwargs)
else:
qs = self.filter(type = Log.Type.play, **kwargs)
if not archives and station.dealer: if not archives and station.dealer:
qs = qs.exclude( qs = qs.exclude(
@ -1247,9 +1245,8 @@ class Log(models.Model):
""" """
play = 0x01 play = 0x01
""" """
Source has been started/changed and is running related_object The related item has been started by the streamer or manually,
If no related_object is available, comment is used to designate and occured on air.
the sound.
""" """
load = 0x02 load = 0x02
""" """
@ -1257,7 +1254,7 @@ class Log(models.Model):
""" """
on_air = 0x03 on_air = 0x03
""" """
A diffusion occured, but in live (no sound played by Aircox) The related item has been detected occuring on air
""" """
other = 0x04 other = 0x04
""" """
@ -1287,6 +1284,12 @@ class Log(models.Model):
default=tz.now, default=tz.now,
db_index = True, db_index = True,
) )
# date of the next diffusion: used in order to ease on_air algo's
end = models.DateTimeField(
_('end'),
default=tz.now,
db_index = True,
)
comment = models.CharField( comment = models.CharField(
_('comment'), _('comment'),
max_length = 512, max_length = 512,
@ -1314,15 +1317,14 @@ class Log(models.Model):
objects = LogManager() objects = LogManager()
@property def estimate_end(self):
def end(self):
""" """
Calculated end using self.related informations Calculated end using self.related informations
""" """
if self.diffusion: if self.diffusion:
return self.diffusion.end return self.diffusion.end
if self.sound: if self.sound:
return self.date + to_timedelta(sound.duration) return self.date + utils.to_timedelta(self.sound.duration)
return self.date return self.date
@property @property
@ -1347,7 +1349,6 @@ class Log(models.Model):
if self.track: if self.track:
r.append('track: ' + str(self.track_id)) r.append('track: ' + str(self.track_id))
logger.info('log #%s: %s%s', logger.info('log #%s: %s%s',
str(self), str(self),
self.comment or '', self.comment or '',
@ -1359,3 +1360,8 @@ class Log(models.Model):
self.pk, self.date.strftime('%Y/%m/%d %H:%M'), self.source self.pk, self.date.strftime('%Y/%m/%d %H:%M'), self.source
) )
def save(self, *args, **kwargs):
if not self.end:
self.end = self.estimate_end()
return super().save(*args, **kwargs)

View File

@ -6,6 +6,9 @@ from django.conf import settings
def ensure (key, default): def ensure (key, default):
globals()[key] = getattr(settings, key, default) globals()[key] = getattr(settings, key, default)
########################################################################
# Global & misc
########################################################################
# group to assign to users at their creation, along with the permissions # group to assign to users at their creation, along with the permissions
# to add to each group. # to add to each group.
ensure('AIRCOX_DEFAULT_USER_GROUPS', { ensure('AIRCOX_DEFAULT_USER_GROUPS', {
@ -23,6 +26,24 @@ ensure('AIRCOX_DEFAULT_USER_GROUPS', {
ensure('AIRCOX_PROGRAMS_DIR', ensure('AIRCOX_PROGRAMS_DIR',
os.path.join(settings.MEDIA_ROOT, 'programs')) os.path.join(settings.MEDIA_ROOT, 'programs'))
# Directory for working data
ensure('AIRCOX_DATA_DIR',
os.path.join(settings.PROJECT_ROOT, 'data'))
########################################################################
# Logs & Archives
########################################################################
# Directory where to save logs' archives
ensure('AIRCOX_LOGS_ARCHIVES_DIR',
os.path.join(AIRCOX_DATA_DIR, 'archives')
)
# In days, minimal age of a log before it is archived
ensure('AIRCOX_LOGS_ARCHIVES_MIN_AGE', 60)
########################################################################
# Sounds
########################################################################
# Default directory for the sounds that not linked to a program # Default directory for the sounds that not linked to a program
ensure('AIRCOX_SOUND_DEFAULT_DIR', ensure('AIRCOX_SOUND_DEFAULT_DIR',
os.path.join(AIRCOX_PROGRAMS_DIR, 'defaults')), os.path.join(AIRCOX_PROGRAMS_DIR, 'defaults')),
@ -54,22 +75,26 @@ ensure(
('.ogg','.flac','.wav','.mp3','.opus') ('.ogg','.flac','.wav','.mp3','.opus')
) )
# Stream for the scheduled diffusions
ensure('AIRCOX_SCHEDULED_STREAM', 0)
# Import playlist: columns for CSV file
ensure(
'AIRCOX_IMPORT_PLAYLIST_CSV_COLS',
('artist', 'title', 'minutes', 'seconds', 'tags', 'info')
)
# Import playlist: column delimiter of csv text files
ensure('AIRCOX_IMPORT_PLAYLIST_CSV_DELIMITER', ';')
# Import playlist: text delimiter of csv text files
ensure('AIRCOX_IMPORT_PLAYLIST_CSV_TEXT_QUOTE', '"')
########################################################################
# Streamer & Controllers
########################################################################
# Controllers working directory # Controllers working directory
ensure('AIRCOX_CONTROLLERS_WORKING_DIR', '/tmp/aircox') ensure('AIRCOX_CONTROLLERS_WORKING_DIR', '/tmp/aircox')
########################################################################
# Playlist import from CSV
########################################################################
# Columns for CSV file
ensure(
'AIRCOX_IMPORT_PLAYLIST_CSV_COLS',
('artist', 'title', 'minutes', 'seconds', 'tags', 'info')
)
# Column delimiter of csv text files
ensure('AIRCOX_IMPORT_PLAYLIST_CSV_DELIMITER', ';')
# Text delimiter of csv text files
ensure('AIRCOX_IMPORT_PLAYLIST_CSV_TEXT_QUOTE', '"')

View File

@ -37,14 +37,17 @@ def on_air(request):
station = request.GET.get('station'); station = request.GET.get('station');
if station: if station:
station = stations.stations.filter(name = station) station = stations.stations.filter(name = station)
if not station.count():
return HttpResponse('')
else: else:
station = stations.stations.first() station = stations.stations
station = station.first()
on_air = station.on_air(count = 10).select_related('track','diffusion') on_air = station.on_air(count = 10).select_related('track','diffusion')
if not on_air.count(): if not on_air.count():
return HttpResponse('') return HttpResponse('')
last = on_air.last() last = on_air.first()
if last.track: if last.track:
last = { last = {
'type': 'track', 'type': 'track',

View File

@ -185,7 +185,7 @@ def diffusion_post_saved(sender, instance, created, *args, **kwargs):
page.save() page.save()
return return
if instance.page: if hasattr(instance, 'page'):
return return
page = models.DiffusionPage.from_diffusion( page = models.DiffusionPage.from_diffusion(