stamina: add "smart" stream errors handling
authorFrédéric Péters <fpeters@0d.be>
Sat, 18 Jul 2020 11:19:09 +0000 (13:19 +0200)
committerFrédéric Péters <fpeters@0d.be>
Sat, 18 Jul 2020 11:19:09 +0000 (13:19 +0200)
nonstop/management/commands/stamina.py
nonstop/migrations/0029_scheduleddiffusion_auto_delayed.py [new file with mode: 0644]
nonstop/models.py

index b604126..42c5df8 100644 (file)
@@ -256,7 +256,56 @@ class Command(BaseCommand):
             if slot.jingle_id:
                 await self.player_process(slot.jingle, timeout=60)
             logger.debug('Stream timeout: %s', (slot.end_datetime - now).total_seconds())
-            await self.player_process(slot, timeout=(slot.end_datetime - now).total_seconds())
+            short_interruption_counter = 0
+            has_played = False
+            while True:
+                player_start_time = datetime.datetime.now()
+                await self.player_process(slot, timeout=(slot.end_datetime - player_start_time).total_seconds())
+                now = datetime.datetime.now()
+                if (slot.end_datetime - now).total_seconds() < 2:
+                    # it went well, stop
+                    break
+                # stream got interrupted
+                if (datetime.datetime.now() - player_start_time).total_seconds() < 15:
+                    # and was up for less than 15 seconds.
+                    if not has_played:
+                        # never up before, probably not even started
+                        if isinstance(slot, RecurringStreamOccurence):
+                            # no mercy for recurring stream, remove occurence
+                            logger.info('Missing stream for %s, removing', slot)
+                            slot.delete()
+                        elif slot.auto_delayed is True:
+                            # was already delayed and is still not up, remove.
+                            logger.info('Still missing stream for %s, removing', slot)
+                            slot.delete()
+                        else:
+                            # push back start datetime for 5 minutes, and get
+                            # back to nonstop music in the meantime
+                            logger.info('Pushing starting time of %s', slot.diffusion.episode)
+                            slot.diffusion.datetime = slot.diffusion.datetime + datetime.timedelta(seconds=300)
+                            slot.diffusion.episode.duration = slot.diffusion.episode.get_duration() - 5
+                            if slot.diffusion.episode.duration <= 5:
+                                slot.diffusion.episode.duration = 0
+                            slot.auto_delayed = True
+                            slot.diffusion.save()
+                            slot.diffusion.episode.save()
+                            slot.save()
+                        break
+                    short_interruption_counter += 1
+                    # wait a bit
+                    await asyncio.sleep(short_interruption_counter)
+                else:
+                    # mark stream as ok at least one, and reset short
+                    # interruption counter
+                    has_played = True
+                    short_interruption_counter = 0
+                    logger.debug('Stream error for %s', slot)
+
+                if short_interruption_counter > 5:
+                    # many short interruptions
+                    logger.info('Too many stream errors for %s, removing', slot)
+                    slot.delete()
+                    break
         else:
             if hasattr(slot, 'episode'):
                 logger.info('Episode: %s (id: %s)', slot.episode, slot.episode.id)
diff --git a/nonstop/migrations/0029_scheduleddiffusion_auto_delayed.py b/nonstop/migrations/0029_scheduleddiffusion_auto_delayed.py
new file mode 100644 (file)
index 0000000..5012eee
--- /dev/null
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.29 on 2020-07-18 13:18
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('nonstop', '0028_auto_20200716_1452'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='scheduleddiffusion',
+            name='auto_delayed',
+            field=models.BooleanField(default=False),
+        ),
+    ]
index a91a8ca..7563104 100644 (file)
@@ -231,6 +231,7 @@ class ScheduledDiffusion(models.Model):
     stream = models.ForeignKey(Stream, null=True, blank=True)
     creation_timestamp = models.DateTimeField(auto_now_add=True, null=True)
     added_to_nonstop_timestamp = models.DateTimeField(null=True)
+    auto_delayed = models.BooleanField(default=False)
 
     def __str__(self):
         return 'Diffusion of %s' % self.diffusion
@@ -324,6 +325,9 @@ class RecurringStreamOccurence(models.Model, RecurringOccurenceMixin):
     diffusion = models.ForeignKey(RecurringStreamDiffusion, on_delete=models.CASCADE)
     datetime = models.DateTimeField(_('Date/time'), db_index=True)
 
+    def __str__(self):
+        return 'Recurring stream of %s' % self.diffusion.stream
+
     @property
     def stream(self):
         return self.diffusion.stream