From 08cb8e71bb90c39811549238dc01324fbccf3152 Mon Sep 17 00:00:00 2001 From: bkfox Date: Sun, 2 Jul 2017 18:09:40 +0200 Subject: [PATCH] fix error in streamer and on_air --- aircox/management/commands/streamer.py | 60 ++++++++++++++++++-------- aircox/models.py | 38 +++++++++------- aircox/settings.py | 53 +++++++++++++++++------ aircox/views.py | 7 ++- aircox_cms/signals.py | 2 +- 5 files changed, 108 insertions(+), 52 deletions(-) diff --git a/aircox/management/commands/streamer.py b/aircox/management/commands/streamer.py index 242df5b..97ec097 100755 --- a/aircox/management/commands/streamer.py +++ b/aircox/management/commands/streamer.py @@ -54,10 +54,18 @@ class Monitor: Datetime of the next sync """ + _last_log = None + """ + Last emitted log + """ + def __init__(self, station, **kwargs): self.station = station self.__dict__.update(kwargs) + self._last_log = Log.objects.station(station).order_by('date') \ + .last() + def monitor(self): """ Run all monitoring functions. @@ -81,6 +89,12 @@ class Monitor: log.save() 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): """ Check the current_sound of the station and update logs if @@ -106,12 +120,12 @@ class Monitor: log.sound.path == current_sound): return - sound = Sound.objects.filter(path = current_sound) + sound = Sound.objects.filter(path = current_sound).first() self.log( - type = Log.Type.play, + type = Log.Type.on_air, source = current_source.id, date = tz.now(), - sound = sound[0] if sound else None, + sound = sound, # keep sound path (if sound is removed, we keep that info) comment = current_sound, ) @@ -135,14 +149,13 @@ class Monitor: now = tz.now() for track in tracks: pos = log.date + tz.timedelta(seconds = track.position) - if pos < now: - self.log( - type = Log.Type.play, - source = log.source, - date = pos, - track = track, - comment = track, - ) + if pos > now: + break + self.log( + type = Log.Type.on_air, source = log.source, + date = pos, track = track, + comment = track, + ) def sync_playlists(self): """ @@ -243,11 +256,16 @@ class Monitor: it is needed. """ dealer = self.station.dealer - if dealer.playlist != playlist: - dealer.playlist = playlist - if diff and not diff.is_live(): - self.log(type = Log.Type.load, source = source.id, - diffusion = diff, date = date) + if dealer.playlist == playlist: + return + + dealer.playlist = playlist + 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): """ @@ -257,18 +275,22 @@ class Monitor: if not diff or diff.start > date: 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 if diff.is_live(): diff_ = Log.objects.station(self.station) \ - .filter(diffusion = diff) + .filter(diffusion = diff, type = Log.Type.on_air) if not diff_.count(): self.log(type = Log.Type.on_air, source = source.id, diffusion = diff, date = date) return # enable dealer - if not dealer.active: - dealer.active = True + if not source.active: + source.active = True self.log(type = Log.Type.play, source = source.id, diffusion = diff, date = date) diff --git a/aircox/models.py b/aircox/models.py index dde706f..3438ca9 100755 --- a/aircox/models.py +++ b/aircox/models.py @@ -278,7 +278,7 @@ class Station(Nameable): for diff in diffs: if count and n >= count: 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 logs = logs.exclude(q, diffusion__isnull = True) @@ -1197,6 +1197,8 @@ class LogManager(models.Manager): def _at(self, date = None, qs = None, **kwargs): start, end = utils.date_range(date) 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) def at(self, station = None, date = None, qs = None, **kwargs): @@ -1207,8 +1209,7 @@ class LogManager(models.Manager): qs = self._at(date, qs, **kwargs) return self.station(station, qs) if station else qs - def played(self, station, archives = True, include_live = True, - **kwargs): + def played(self, station, archives = True, **kwargs): """ Return a queryset of the played elements' log for the given 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 * kwargs: extra filter kwargs """ - if include_live: - qs = self.filter(type__in = (Log.Type.play, Log.Type.on_air), - **kwargs) - else: - qs = self.filter(type = Log.Type.play, **kwargs) + qs = self.filter(type__in = (Log.Type.play, Log.Type.on_air), + **kwargs) if not archives and station.dealer: qs = qs.exclude( @@ -1247,9 +1245,8 @@ class Log(models.Model): """ play = 0x01 """ - Source has been started/changed and is running related_object - If no related_object is available, comment is used to designate - the sound. + The related item has been started by the streamer or manually, + and occured on air. """ load = 0x02 """ @@ -1257,7 +1254,7 @@ class Log(models.Model): """ 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 """ @@ -1287,6 +1284,12 @@ class Log(models.Model): default=tz.now, 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'), max_length = 512, @@ -1314,15 +1317,14 @@ class Log(models.Model): objects = LogManager() - @property - def end(self): + def estimate_end(self): """ Calculated end using self.related informations """ if self.diffusion: return self.diffusion.end if self.sound: - return self.date + to_timedelta(sound.duration) + return self.date + utils.to_timedelta(self.sound.duration) return self.date @property @@ -1347,7 +1349,6 @@ class Log(models.Model): if self.track: r.append('track: ' + str(self.track_id)) - logger.info('log #%s: %s%s', str(self), self.comment or '', @@ -1359,3 +1360,8 @@ class Log(models.Model): 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) + diff --git a/aircox/settings.py b/aircox/settings.py index 46abbb4..00879b1 100755 --- a/aircox/settings.py +++ b/aircox/settings.py @@ -6,6 +6,9 @@ from django.conf import settings def ensure (key, default): globals()[key] = getattr(settings, key, default) +######################################################################## +# Global & misc +######################################################################## # group to assign to users at their creation, along with the permissions # to add to each group. ensure('AIRCOX_DEFAULT_USER_GROUPS', { @@ -23,6 +26,24 @@ ensure('AIRCOX_DEFAULT_USER_GROUPS', { ensure('AIRCOX_PROGRAMS_DIR', 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 ensure('AIRCOX_SOUND_DEFAULT_DIR', os.path.join(AIRCOX_PROGRAMS_DIR, 'defaults')), @@ -54,22 +75,26 @@ ensure( ('.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 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', '"') + + + diff --git a/aircox/views.py b/aircox/views.py index 3c9c3f7..7f13bbe 100755 --- a/aircox/views.py +++ b/aircox/views.py @@ -37,14 +37,17 @@ def on_air(request): station = request.GET.get('station'); if station: station = stations.stations.filter(name = station) + if not station.count(): + return HttpResponse('') else: - station = stations.stations.first() + station = stations.stations + station = station.first() on_air = station.on_air(count = 10).select_related('track','diffusion') if not on_air.count(): return HttpResponse('') - last = on_air.last() + last = on_air.first() if last.track: last = { 'type': 'track', diff --git a/aircox_cms/signals.py b/aircox_cms/signals.py index 2588098..831fd25 100755 --- a/aircox_cms/signals.py +++ b/aircox_cms/signals.py @@ -185,7 +185,7 @@ def diffusion_post_saved(sender, instance, created, *args, **kwargs): page.save() return - if instance.page: + if hasattr(instance, 'page'): return page = models.DiffusionPage.from_diffusion(