|
10 | 10 | CHUNK_SIZE = 25 * 1024 |
11 | 11 | TAG = "[KortexaRadio]" |
12 | 12 |
|
13 | | -CONTINUE_PROMPT = "Say 'play' to start the radio, or 'stop' to exit." |
| 13 | +STOP_WORDS = ["stop", "off", "exit", "quit", "turn it off"] |
14 | 14 |
|
15 | 15 |
|
16 | 16 | class KortexaRadioCapability(MatchingCapability): |
@@ -75,113 +75,61 @@ async def _keep_events_connected(self): |
75 | 75 | pass |
76 | 76 |
|
77 | 77 | async def run(self): |
78 | | - """Main setup and conversation loop.""" |
| 78 | + """Auto-start radio, listen for stop command, exit cleanly.""" |
79 | 79 |
|
80 | | - first_time = True |
81 | | - is_playing = False |
82 | | - is_stopping = False |
| 80 | + stream_task = None |
83 | 81 | events_task = None |
84 | 82 |
|
85 | 83 | try: |
86 | | - while True: |
87 | | - if is_stopping: |
88 | | - self.worker.editor_logging_handler.info(f"{TAG} Waiting for stream to finish") |
89 | | - |
90 | | - await self.worker.session_tasks.sleep(0.1) |
91 | | - |
92 | | - if not events_task: |
93 | | - self.worker.editor_logging_handler.info(f"{TAG} No events task") |
94 | | - |
95 | | - break |
96 | | - |
97 | | - continue |
98 | | - |
99 | | - if first_time: |
100 | | - self.worker.editor_logging_handler.info(f"{TAG} First trigger") |
101 | | - |
102 | | - msg = "start" |
103 | | - first_time = False |
104 | | - else: |
105 | | - self.worker.editor_logging_handler.info(f"{TAG} Interruption") |
106 | | - |
107 | | - msg = await self.capability_worker.user_response() |
108 | | - |
109 | | - if not msg or not msg.strip(): |
110 | | - self.worker.editor_logging_handler.info(f"{TAG} User silent") |
111 | | - |
112 | | - if is_playing: |
113 | | - msg = "stop" |
114 | | - else: |
115 | | - msg = "start" |
116 | | - |
117 | | - normalized = msg.strip().lower() |
118 | | - |
119 | | - self.worker.editor_logging_handler.info(f"{TAG} Command: {normalized}") |
120 | | - |
121 | | - if "stop" in normalized or "off" in normalized: |
122 | | - is_stopping = True |
123 | | - |
124 | | - self.worker.editor_logging_handler.info(f"{TAG} Stop stream") |
| 84 | + await self.capability_worker.speak("Tuning in to Kortexa Radio.") |
125 | 85 |
|
126 | | - self.worker.music_mode_stop_event.set() |
| 86 | + # Subscribe to events (registers us as a listener) |
| 87 | + self.worker.editor_logging_handler.info(f"{TAG} Register as a listener") |
| 88 | + events_task = self.worker.session_tasks.create(self._keep_events_connected()) |
127 | 89 |
|
128 | | - self.worker.editor_logging_handler.info(f"{TAG} Turn off music mode") |
| 90 | + # Turn on music mode |
| 91 | + self.worker.editor_logging_handler.info(f"{TAG} Turn on music mode") |
| 92 | + self.worker.music_mode_event.set() |
| 93 | + await self.capability_worker.send_data_over_websocket("music-mode", {"mode": "on"}) |
129 | 94 |
|
130 | | - await self.capability_worker.send_data_over_websocket("music-mode", {"mode": "off"}) |
131 | | - self.worker.music_mode_event.clear() |
| 95 | + # Stream audio in background so the loop stays responsive |
| 96 | + self.worker.editor_logging_handler.info(f"{TAG} Start streaming") |
| 97 | + stream_task = self.worker.session_tasks.create(self._stream()) |
132 | 98 |
|
133 | | - elif "play" in normalized or "start" in normalized or "on" in normalized or "radio" in normalized: |
134 | | - self.worker.editor_logging_handler.info(f"{TAG} Start playing") |
| 99 | + # Wait for stop command |
| 100 | + while not self.worker.music_mode_stop_event.is_set(): |
| 101 | + msg = await self.capability_worker.user_response() |
135 | 102 |
|
136 | | - is_playing = True |
137 | | - |
138 | | - await self.capability_worker.speak("Tuning in to Kortexa Radio.") |
139 | | - |
140 | | - # Subscribe to events (registers us as a listener) |
141 | | - self.worker.editor_logging_handler.info(f"{TAG} Register as a listener") |
142 | | - events_task = self.worker.session_tasks.create(self._keep_events_connected()) |
143 | | - |
144 | | - self.worker.editor_logging_handler.info(f"{TAG} Turn on music mode") |
145 | | - |
146 | | - self.worker.music_mode_event.set() |
147 | | - await self.capability_worker.send_data_over_websocket("music-mode", {"mode": "on"}) |
148 | | - |
149 | | - # Stream audio (blocks until stop event or error) |
150 | | - await self._stream() |
151 | | - |
152 | | - if events_task: |
153 | | - self.worker.editor_logging_handler.info(f"{TAG} Unregister as a listener") |
154 | | - |
155 | | - events_task.cancel() |
156 | | - events_task = None |
157 | | - |
158 | | - await self.capability_worker.speak("Radio off! Catch you later.") |
159 | | - |
160 | | - self.worker.music_mode_stop_event.clear() |
| 103 | + if self.worker.music_mode_stop_event.is_set(): |
| 104 | + break |
161 | 105 |
|
162 | | - is_playing = False |
| 106 | + if msg: |
| 107 | + normalized = msg.strip().lower() |
| 108 | + self.worker.editor_logging_handler.info(f"{TAG} Command: {normalized}") |
163 | 109 |
|
164 | | - self.worker.editor_logging_handler.info(f"{TAG} Stop playing") |
| 110 | + if any(word in normalized for word in STOP_WORDS): |
| 111 | + self.worker.editor_logging_handler.info(f"{TAG} Stop requested") |
| 112 | + self.worker.music_mode_stop_event.set() |
| 113 | + break |
165 | 114 |
|
166 | | - break |
| 115 | + await self.capability_worker.speak("Radio off! Catch you later.") |
167 | 116 |
|
168 | 117 | except Exception as e: |
169 | 118 | self.worker.editor_logging_handler.error(f"{TAG} Error: {e}") |
170 | 119 |
|
171 | 120 | finally: |
172 | 121 | self.worker.editor_logging_handler.info(f"{TAG} Clean up") |
173 | 122 |
|
174 | | - if events_task: |
175 | | - self.worker.editor_logging_handler.info(f"{TAG} Unregister as a listener") |
| 123 | + self.worker.music_mode_stop_event.set() |
176 | 124 |
|
| 125 | + if stream_task: |
| 126 | + stream_task.cancel() |
| 127 | + if events_task: |
177 | 128 | events_task.cancel() |
178 | | - events_task = None |
179 | | - |
180 | | - if is_playing: |
181 | | - self.worker.editor_logging_handler.info(f"{TAG} Turn off music mode") |
182 | 129 |
|
183 | | - await self.capability_worker.send_data_over_websocket("music-mode", {"mode": "off"}) |
184 | | - self.worker.music_mode_event.clear() |
| 130 | + await self.capability_worker.send_data_over_websocket("music-mode", {"mode": "off"}) |
| 131 | + self.worker.music_mode_event.clear() |
| 132 | + self.worker.music_mode_stop_event.clear() |
185 | 133 |
|
186 | 134 | self.worker.editor_logging_handler.info(f"{TAG} Radio OFF") |
187 | 135 |
|
|
0 commit comments