We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
check_live_state pop from empty list
## Bug Report

**Problematic Behavior**

The management command check_live_state has a recurring error, it tries to pop a value from an empty list. This list comes from cloudwatch service : https://github.com/openfun/marsha/blob/29e1f78ed6e288f7bba3c198bb7b7179e7af4fe0/src/backend/marsha/core/management/commands/check_live_state.py#L100

**Expected behavior/code**

This error seems to occur when a live has no activity anymore. We try to compare alerts set and the clear to determine if they are still active.


**Steps to Reproduce**
1. Start a webinar
2. Once started, stop all activity
3. Run the management command `check_live_state`
4. And then the bug happens!



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/backend/marsha/core/management/commands/check_live_state.py`
Content:
```
1 """Check live state management command."""
2 
3 from datetime import datetime, timedelta, timezone
4 import json
5 import re
6 
7 from django.conf import settings
8 from django.core.management.base import BaseCommand
9 
10 import boto3
11 from dateutil.parser import isoparse
12 
13 from marsha.core.defaults import RUNNING, STOPPING
14 from marsha.core.models import Video
15 from marsha.core.utils.medialive_utils import stop_live_channel
16 
17 
18 aws_credentials = {
19     "aws_access_key_id": settings.AWS_ACCESS_KEY_ID,
20     "aws_secret_access_key": settings.AWS_SECRET_ACCESS_KEY,
21     "region_name": settings.AWS_S3_REGION_NAME,
22 }
23 
24 # Configure medialive client
25 medialive_client = boto3.client("medialive", **aws_credentials)
26 
27 # Configure cloudwatch logs client
28 logs_client = boto3.client("logs", **aws_credentials)
29 
30 
31 def parse_iso_date(iso_date):
32     """Parse an iso 8601 date and return a datetime object."""
33     return isoparse(iso_date)
34 
35 
36 def generate_expired_date():
37     """Generate a datetime object 25 minutes in the past."""
38     return datetime.now(tz=timezone.utc) - timedelta(minutes=25)
39 
40 
41 # pylint: disable=too-many-locals
42 class Command(BaseCommand):
43     """Check every live streaming running state on AWS."""
44 
45     help = (
46         "Check activity on AWS for every live streaming running"
47         "and close them if there is not."
48     )
49 
50     def handle(self, *args, **options):
51         """Execute management command."""
52         extract_message_pattern = (
53             r"^(?P<ingestion_time>.*)\t"
54             r"(?P<request_id>.*)\t"
55             r"(?P<level>.*)\t"
56             r"Received event:(?P<message>.*)$"
57         )
58         extract_message_regex = re.compile(extract_message_pattern)
59 
60         videos = Video.objects.filter(live_state=RUNNING)
61         for video in videos:
62             # For each running live video, we query cloudwatch on the current live
63             # to search messages having detail.alert_type set to `RTMP Has No Audio/Video`.
64             # This alert tell us there is no stream and the live can be stopped if the message is
65             # older than 25 minutes.
66             self.stdout.write(f"Checking video {video.id}")
67             live_info = video.live_info
68             logs = logs_client.filter_log_events(
69                 logGroupName=live_info["cloudwatch"]["logGroupName"],
70                 startTime=int(int(video.live_info.get("started_at")) * 1000),
71                 filterPattern=(
72                     "{"
73                     '($.detail-type = "MediaLive Channel Alert") && '
74                     f"($.resources[0] = \"{live_info['medialive']['channel']['arn']}\") &&"
75                     '($.detail.alert_type = "RTMP Has No Audio/Video")'
76                     "}"
77                 ),
78             )
79 
80             pipelines_queue = {"0": [], "1": []}
81 
82             for event in logs["events"]:
83                 # All events must be parsed to extract the JSON message. When an alert is added,
84                 # the `alarm_state` property value is `SET` and when the alert is removed,
85                 # the `alarm_state` property value is `CLEARED`.
86                 # We have 2 pipelines, a live is over when the 2 pipeline have `SET` value
87                 # in `alarm_state`.
88                 # Alarm state act like a list with all the event history. It means a `CLEARED`
89                 # event is related to a `SET` one. So we have to look over all events, put in
90                 # a list all `SET` events and remove it if a `CLEARED` event is here. At the
91                 # end if we have 2 `SET` events, the live has no activity and we have to check
92                 # the time of the last `SET` event. If this time is older than 25 minutes we
93                 # stop the channel.
94                 log = extract_message_regex.match(event["message"])
95                 message = json.loads(log.group("message"))
96 
97                 if message["detail"]["alarm_state"] == "SET":
98                     pipelines_queue[message["detail"]["pipeline"]].append(message)
99                 else:
100                     pipelines_queue[message["detail"]["pipeline"]].pop()
101 
102             if len(pipelines_queue["0"]) == 1 and len(pipelines_queue["1"]) == 1:
103                 # Both pipelines receive no stream, we have to check the more recent one
104                 # and if the time is older than 25 minutes we stop the channel.
105                 datetime_pipeline0 = parse_iso_date(pipelines_queue["0"][0]["time"])
106                 datetime_pipeline1 = parse_iso_date(pipelines_queue["1"][0]["time"])
107                 expired_date = generate_expired_date()
108 
109                 if (
110                     datetime_pipeline0 < expired_date
111                     or datetime_pipeline1 < expired_date
112                 ):
113                     # Stop this channel
114                     self.stdout.write(
115                         f"Stopping channel with id {live_info['medialive']['channel']['id']}"
116                     )
117                     stop_live_channel(live_info["medialive"]["channel"]["id"])
118 
119                     video.live_state = STOPPING
120                     video.save()
121                     self.stdout.write("Channel stopped")
122
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/backend/marsha/core/management/commands/check_live_state.py b/src/backend/marsha/core/management/commands/check_live_state.py
--- a/src/backend/marsha/core/management/commands/check_live_state.py
+++ b/src/backend/marsha/core/management/commands/check_live_state.py
@@ -67,7 +67,7 @@
             live_info = video.live_info
             logs = logs_client.filter_log_events(
                 logGroupName=live_info["cloudwatch"]["logGroupName"],
-                startTime=int(int(video.live_info.get("started_at")) * 1000),
+                startTime=int((int(video.live_info.get("started_at")) - 60) * 1000),
                 filterPattern=(
                     "{"
                     '($.detail-type = "MediaLive Channel Alert") && '

{"golden_diff": "diff --git a/src/backend/marsha/core/management/commands/check_live_state.py b/src/backend/marsha/core/management/commands/check_live_state.py\n--- a/src/backend/marsha/core/management/commands/check_live_state.py\n+++ b/src/backend/marsha/core/management/commands/check_live_state.py\n@@ -67,7 +67,7 @@\n             live_info = video.live_info\n             logs = logs_client.filter_log_events(\n                 logGroupName=live_info[\"cloudwatch\"][\"logGroupName\"],\n-                startTime=int(int(video.live_info.get(\"started_at\")) * 1000),\n+                startTime=int((int(video.live_info.get(\"started_at\")) - 60) * 1000),\n                 filterPattern=(\n                     \"{\"\n                     '($.detail-type = \"MediaLive Channel Alert\") && '\n", "issue": "check_live_state pop from empty list\n## Bug Report\r\n\r\n**Problematic Behavior**\r\n\r\nThe management command check_live_state has a recurring error, it tries to pop a value from an empty list. This list comes from cloudwatch service : https://github.com/openfun/marsha/blob/29e1f78ed6e288f7bba3c198bb7b7179e7af4fe0/src/backend/marsha/core/management/commands/check_live_state.py#L100\r\n\r\n**Expected behavior/code**\r\n\r\nThis error seems to occur when a live has no activity anymore. We try to compare alerts set and the clear to determine if they are still active.\r\n\r\n\r\n**Steps to Reproduce**\r\n1. Start a webinar\r\n2. Once started, stop all activity\r\n3. Run the management command `check_live_state`\r\n4. And then the bug happens!\r\n\r\n\n", "before_files": [{"content": "\"\"\"Check live state management command.\"\"\"\n\nfrom datetime import datetime, timedelta, timezone\nimport json\nimport re\n\nfrom django.conf import settings\nfrom django.core.management.base import BaseCommand\n\nimport boto3\nfrom dateutil.parser import isoparse\n\nfrom marsha.core.defaults import RUNNING, STOPPING\nfrom marsha.core.models import Video\nfrom marsha.core.utils.medialive_utils import stop_live_channel\n\n\naws_credentials = {\n    \"aws_access_key_id\": settings.AWS_ACCESS_KEY_ID,\n    \"aws_secret_access_key\": settings.AWS_SECRET_ACCESS_KEY,\n    \"region_name\": settings.AWS_S3_REGION_NAME,\n}\n\n# Configure medialive client\nmedialive_client = boto3.client(\"medialive\", **aws_credentials)\n\n# Configure cloudwatch logs client\nlogs_client = boto3.client(\"logs\", **aws_credentials)\n\n\ndef parse_iso_date(iso_date):\n    \"\"\"Parse an iso 8601 date and return a datetime object.\"\"\"\n    return isoparse(iso_date)\n\n\ndef generate_expired_date():\n    \"\"\"Generate a datetime object 25 minutes in the past.\"\"\"\n    return datetime.now(tz=timezone.utc) - timedelta(minutes=25)\n\n\n# pylint: disable=too-many-locals\nclass Command(BaseCommand):\n    \"\"\"Check every live streaming running state on AWS.\"\"\"\n\n    help = (\n        \"Check activity on AWS for every live streaming running\"\n        \"and close them if there is not.\"\n    )\n\n    def handle(self, *args, **options):\n        \"\"\"Execute management command.\"\"\"\n        extract_message_pattern = (\n            r\"^(?P<ingestion_time>.*)\\t\"\n            r\"(?P<request_id>.*)\\t\"\n            r\"(?P<level>.*)\\t\"\n            r\"Received event:(?P<message>.*)$\"\n        )\n        extract_message_regex = re.compile(extract_message_pattern)\n\n        videos = Video.objects.filter(live_state=RUNNING)\n        for video in videos:\n            # For each running live video, we query cloudwatch on the current live\n            # to search messages having detail.alert_type set to `RTMP Has No Audio/Video`.\n            # This alert tell us there is no stream and the live can be stopped if the message is\n            # older than 25 minutes.\n            self.stdout.write(f\"Checking video {video.id}\")\n            live_info = video.live_info\n            logs = logs_client.filter_log_events(\n                logGroupName=live_info[\"cloudwatch\"][\"logGroupName\"],\n                startTime=int(int(video.live_info.get(\"started_at\")) * 1000),\n                filterPattern=(\n                    \"{\"\n                    '($.detail-type = \"MediaLive Channel Alert\") && '\n                    f\"($.resources[0] = \\\"{live_info['medialive']['channel']['arn']}\\\") &&\"\n                    '($.detail.alert_type = \"RTMP Has No Audio/Video\")'\n                    \"}\"\n                ),\n            )\n\n            pipelines_queue = {\"0\": [], \"1\": []}\n\n            for event in logs[\"events\"]:\n                # All events must be parsed to extract the JSON message. When an alert is added,\n                # the `alarm_state` property value is `SET` and when the alert is removed,\n                # the `alarm_state` property value is `CLEARED`.\n                # We have 2 pipelines, a live is over when the 2 pipeline have `SET` value\n                # in `alarm_state`.\n                # Alarm state act like a list with all the event history. It means a `CLEARED`\n                # event is related to a `SET` one. So we have to look over all events, put in\n                # a list all `SET` events and remove it if a `CLEARED` event is here. At the\n                # end if we have 2 `SET` events, the live has no activity and we have to check\n                # the time of the last `SET` event. If this time is older than 25 minutes we\n                # stop the channel.\n                log = extract_message_regex.match(event[\"message\"])\n                message = json.loads(log.group(\"message\"))\n\n                if message[\"detail\"][\"alarm_state\"] == \"SET\":\n                    pipelines_queue[message[\"detail\"][\"pipeline\"]].append(message)\n                else:\n                    pipelines_queue[message[\"detail\"][\"pipeline\"]].pop()\n\n            if len(pipelines_queue[\"0\"]) == 1 and len(pipelines_queue[\"1\"]) == 1:\n                # Both pipelines receive no stream, we have to check the more recent one\n                # and if the time is older than 25 minutes we stop the channel.\n                datetime_pipeline0 = parse_iso_date(pipelines_queue[\"0\"][0][\"time\"])\n                datetime_pipeline1 = parse_iso_date(pipelines_queue[\"1\"][0][\"time\"])\n                expired_date = generate_expired_date()\n\n                if (\n                    datetime_pipeline0 < expired_date\n                    or datetime_pipeline1 < expired_date\n                ):\n                    # Stop this channel\n                    self.stdout.write(\n                        f\"Stopping channel with id {live_info['medialive']['channel']['id']}\"\n                    )\n                    stop_live_channel(live_info[\"medialive\"][\"channel\"][\"id\"])\n\n                    video.live_state = STOPPING\n                    video.save()\n                    self.stdout.write(\"Channel stopped\")\n", "path": "src/backend/marsha/core/management/commands/check_live_state.py"}], "after_files": [{"content": "\"\"\"Check live state management command.\"\"\"\n\nfrom datetime import datetime, timedelta, timezone\nimport json\nimport re\n\nfrom django.conf import settings\nfrom django.core.management.base import BaseCommand\n\nimport boto3\nfrom dateutil.parser import isoparse\n\nfrom marsha.core.defaults import RUNNING, STOPPING\nfrom marsha.core.models import Video\nfrom marsha.core.utils.medialive_utils import stop_live_channel\n\n\naws_credentials = {\n    \"aws_access_key_id\": settings.AWS_ACCESS_KEY_ID,\n    \"aws_secret_access_key\": settings.AWS_SECRET_ACCESS_KEY,\n    \"region_name\": settings.AWS_S3_REGION_NAME,\n}\n\n# Configure medialive client\nmedialive_client = boto3.client(\"medialive\", **aws_credentials)\n\n# Configure cloudwatch logs client\nlogs_client = boto3.client(\"logs\", **aws_credentials)\n\n\ndef parse_iso_date(iso_date):\n    \"\"\"Parse an iso 8601 date and return a datetime object.\"\"\"\n    return isoparse(iso_date)\n\n\ndef generate_expired_date():\n    \"\"\"Generate a datetime object 25 minutes in the past.\"\"\"\n    return datetime.now(tz=timezone.utc) - timedelta(minutes=25)\n\n\n# pylint: disable=too-many-locals\nclass Command(BaseCommand):\n    \"\"\"Check every live streaming running state on AWS.\"\"\"\n\n    help = (\n        \"Check activity on AWS for every live streaming running\"\n        \"and close them if there is not.\"\n    )\n\n    def handle(self, *args, **options):\n        \"\"\"Execute management command.\"\"\"\n        extract_message_pattern = (\n            r\"^(?P<ingestion_time>.*)\\t\"\n            r\"(?P<request_id>.*)\\t\"\n            r\"(?P<level>.*)\\t\"\n            r\"Received event:(?P<message>.*)$\"\n        )\n        extract_message_regex = re.compile(extract_message_pattern)\n\n        videos = Video.objects.filter(live_state=RUNNING)\n        for video in videos:\n            # For each running live video, we query cloudwatch on the current live\n            # to search messages having detail.alert_type set to `RTMP Has No Audio/Video`.\n            # This alert tell us there is no stream and the live can be stopped if the message is\n            # older than 25 minutes.\n            self.stdout.write(f\"Checking video {video.id}\")\n            live_info = video.live_info\n            logs = logs_client.filter_log_events(\n                logGroupName=live_info[\"cloudwatch\"][\"logGroupName\"],\n                startTime=int((int(video.live_info.get(\"started_at\")) - 60) * 1000),\n                filterPattern=(\n                    \"{\"\n                    '($.detail-type = \"MediaLive Channel Alert\") && '\n                    f\"($.resources[0] = \\\"{live_info['medialive']['channel']['arn']}\\\") &&\"\n                    '($.detail.alert_type = \"RTMP Has No Audio/Video\")'\n                    \"}\"\n                ),\n            )\n\n            pipelines_queue = {\"0\": [], \"1\": []}\n\n            for event in logs[\"events\"]:\n                # All events must be parsed to extract the JSON message. When an alert is added,\n                # the `alarm_state` property value is `SET` and when the alert is removed,\n                # the `alarm_state` property value is `CLEARED`.\n                # We have 2 pipelines, a live is over when the 2 pipeline have `SET` value\n                # in `alarm_state`.\n                # Alarm state act like a list with all the event history. It means a `CLEARED`\n                # event is related to a `SET` one. So we have to look over all events, put in\n                # a list all `SET` events and remove it if a `CLEARED` event is here. At the\n                # end if we have 2 `SET` events, the live has no activity and we have to check\n                # the time of the last `SET` event. If this time is older than 25 minutes we\n                # stop the channel.\n                log = extract_message_regex.match(event[\"message\"])\n                message = json.loads(log.group(\"message\"))\n\n                if message[\"detail\"][\"alarm_state\"] == \"SET\":\n                    pipelines_queue[message[\"detail\"][\"pipeline\"]].append(message)\n                else:\n                    pipelines_queue[message[\"detail\"][\"pipeline\"]].pop()\n\n            if len(pipelines_queue[\"0\"]) == 1 and len(pipelines_queue[\"1\"]) == 1:\n                # Both pipelines receive no stream, we have to check the more recent one\n                # and if the time is older than 25 minutes we stop the channel.\n                datetime_pipeline0 = parse_iso_date(pipelines_queue[\"0\"][0][\"time\"])\n                datetime_pipeline1 = parse_iso_date(pipelines_queue[\"1\"][0][\"time\"])\n                expired_date = generate_expired_date()\n\n                if (\n                    datetime_pipeline0 < expired_date\n                    or datetime_pipeline1 < expired_date\n                ):\n                    # Stop this channel\n                    self.stdout.write(\n                        f\"Stopping channel with id {live_info['medialive']['channel']['id']}\"\n                    )\n                    stop_live_channel(live_info[\"medialive\"][\"channel\"][\"id\"])\n\n                    video.live_state = STOPPING\n                    video.save()\n                    self.stdout.write(\"Channel stopped\")\n", "path": "src/backend/marsha/core/management/commands/check_live_state.py"}]}