Files
Atomcms-edit/app/Http/Controllers/Radio/SseController.php
T

184 lines
5.4 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace App\Http\Controllers\Radio;
use App\Enums\RadioSettings;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Concerns\HasRadioSettings;
use App\Services\Community\RadioScheduleService;
use App\Services\Community\RadioStreamService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\StreamedResponse;
class SseController extends Controller
{
use HasRadioSettings;
private const int SSE_KEEPALIVE = 15;
public function __construct(
private readonly RadioStreamService $streamService,
private readonly RadioScheduleService $scheduleService,
) {}
public function stream(Request $request): StreamedResponse
{
$channels = $request->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;
}
}