query('channels', 'now-playing,listeners,dj'); $wanted = array_flip(array_map('trim', explode(',', $channels))); $response = new StreamedResponse(function () use ($wanted): void { $last = [ 'now-playing' => null, 'listeners' => null, 'dj' => null, ]; $this->sendSseComment('connected'); while (! connection_aborted()) { $sent = false; if (isset($wanted['now-playing'])) { $data = $this->getNowPlaying(); if ($data !== $last['now-playing']) { $this->sendSseEvent('now-playing', $data); $last['now-playing'] = $data; $sent = true; } } if (isset($wanted['listeners'])) { $count = $this->getListeners(); if ($count !== $last['listeners']) { $this->sendSseEvent('listeners', ['count' => $count]); $last['listeners'] = $count; $sent = true; } } if (isset($wanted['dj'])) { $dj = $this->getCurrentDj(); if ($dj !== $last['dj']) { $this->sendSseEvent('dj', $dj); $last['dj'] = $dj; $sent = true; } } if (! $sent) { $this->sendSseComment('keepalive'); } ob_flush(); flush(); sleep(5); } }); $response->headers->set('Content-Type', 'text/event-stream'); $response->headers->set('Cache-Control', 'no-cache'); $response->headers->set('Connection', 'keep-alive'); $response->headers->set('X-Accel-Buffering', 'no'); return $response; } private function sendSseEvent(string $event, mixed $data): void { echo "event: {$event}\n"; echo 'data: ' . json_encode($data) . "\n\n"; } private function sendSseComment(string $comment): void { echo ": {$comment}\n\n"; } private function getNowPlaying(): ?array { $autoDj = Cache::get('radio_auto_dj_active'); if ($autoDj !== null) { return [ 'enabled' => true, 'song' => $autoDj['title'], 'artist' => $autoDj['artist'] ?? null, 'title' => $autoDj['title'], 'is_auto_dj' => true, ]; } $cached = Cache::get('radio_nowplaying'); if ($cached !== null) { $cached['is_auto_dj'] = false; return $cached; } $apiUrl = $this->getSetting(RadioSettings::NowPlayingEnabled) ? ($this->getSetting(RadioSettings::NowPlayingApiUrl) ?: $this->getAzureCastApiUrl()) : null; $result = $apiUrl ? $this->streamService->getNowPlaying($apiUrl) : ['enabled' => false, 'song' => null]; $result['is_auto_dj'] = false; Cache::put('radio_nowplaying', $result, 10); return $result; } private function getListeners(): int { return (int) Cache::remember('radio_listeners', 30, function () { $apiUrl = $this->getSetting(RadioSettings::ListenersEnabled) ? ($this->getSetting(RadioSettings::ListenersApiUrl) ?: $this->getAzureCastApiUrl()) : null; return $apiUrl ? $this->streamService->getListenersCount($apiUrl) : 0; }); } private function getCurrentDj(): ?array { $autoDj = Cache::get('radio_auto_dj_active'); $dj = $this->scheduleService->getCurrentDJ( $this->getSetting(RadioSettings::CurrentDjId) ); if ($dj === null && $autoDj !== null) { return [ 'username' => 'Auto DJ', 'look' => null, 'show_name' => 'Auto DJ', 'is_auto_dj' => true, 'song' => $autoDj['title'] ?? null, ]; } if ($dj !== null) { $dj['is_auto_dj'] = false; } return $dj; } private function getAzureCastApiUrl(): ?string { $baseUrl = $this->getSetting(RadioSettings::AzureCastBaseUrl); $stationId = (int) $this->getSetting(RadioSettings::AzureCastStationId, '1'); if (! $baseUrl) { return null; } return rtrim($baseUrl, '/') . '/api/nowplaying/' . $stationId; } }