so, I was trying to create a way via Laravel/Livewire to create some sort of Web Socket or otherwise continuous “communicate” with my API end-point. Basically I’m creating a Web Radio that allows me to listen to songs via AzuraCast. The API response gives me a lot of information including, “seconds_remaining”, “seconds_total”, “elapsed_second”. The fact is that I would like to be able to update my API, using a sort of Timer that, at the end of these “remaining_seconds”, my component has a refresh without “influencing” the Network Service too much.
I’m currently trying to update my component like this:
class TitleSong extends Component
{
public $loading = true;
public $TimePolling;
public $elementToShow = '';
public array $cachedData = [];
public int $remainingTime;
public $error = 'Something went wrong, try again...';
public $audioURL;
public function render()
{
return view('livewire.title-song');
}
public function mount(): void
{
$this->cachedData = Cache::get('song_data', [
'title' => '',
'artist' => '',
'image' => '',
'total_seconds' => 0,
'seconds_elapsed' => 0,
'seconds_remaining' => 0,
'spotifyURL' => '',
'audioURL' => '',
]);
/* if ($this->cachedData) {
$this->fetchSongData();
$this->loading = false;
$this->remainingTime = $this->cachedData['total_seconds'] - $this->cachedData['seconds_elapsed'];
} */
}
public function fetchSongData()
{
try {
$response = Http::get('http://138.197.88.112/api/proc/s/currently_playing');
if (!$response->successful()) {
throw new Exception('Failed to fetch song data.');
}
$data = $response->json()['song'];
$this->TimePolling = $data['seconds_remaining'];
// Update the component's state
if ($data['title'] !== $this->cachedData['title'] || $data['artist'] !== $this->cachedData['artist']) {
$this->cachedData = [
'title' => $data['title'],
'artist' => $data['artist'],
'image' => $data['art'],
'total_seconds' => $data['seconds_total'],
'seconds_elapsed' => $data['seconds_elapsed'],
'seconds_remaining' => $data['seconds_remaining'],
'spotifyURL' => $data['url'],
'audioURL' => 'https://stream.repeatradio.net/',
];
$this->loading = false;
// Cache the updated data
Cache::put('song_data', $this->cachedData);
}
} catch (Throwable $th) {
Log::error($th->getMessage());
$this->handleFetchError();
}
}
private function handleFetchError()
{
$this->error = 'Something went wrong. Please try again later.';
$this->cachedData = [
'title' => $this->error,
'artist' => $this->error,
'image' => '',
'total_seconds' => 0,
'seconds_elapsed' => 0,
'audioURL' => '',
'spotifyURL'=> '',
];
$this->loading = false;
}
Then in the view simply I display:
<div id="title-song" wire:poll.{{ $TimePolling }}s="fetchSongData">
<!-- Actual Content -->
<!-- IF THERE IS AN ERROR -->
@if ($error && !$cachedData)
@if ($elementToShow === 'songTitle')
<h1 id="songTitle" style="font-weight: 600;" class="text-4xl text-red-600">{{ $error }}</h1>
@elseif ($elementToShow === 'songArtist')
<p id="songArtist" class="mt-2 ml-1 text-red-600" style="font-weight: 400; font-size: 15px">{{ $error }}
</p>
@elseif ($elementToShow === 'secondsTotal')
<span id="secondsTotal" class="total-time">0</span>
@elseif($elementToShow === 'songImage')
<img id="artImage" class="text-center"
style="position: absolute; left: 12%; top: 2%; border-radius:20px; z-index: 1; line-height:50%;"
width="220px" height="200px" alt="No image found" />
@elseif($elementToShow === 'audioURL')
<audio src="" id="audio"></audio>
@endif
@else
<!-- IF THERE IS NO ERROR -->
@if ($elementToShow === 'songTitle')
<h1 id="songTitle" style="color: var(--quaternary-color); font-weight: 600" class="text-4xl">
{{ $cachedData['title'] }}</h1>
@elseif ($elementToShow === 'songArtist')
<p id="songArtist" class="mt-2 ml-1" style="color: var(--quinary-color); font-weight: 400; font-size: 15px">
{{ $cachedData['artist'] }}</p>
@elseif ($elementToShow === 'secondsTotal')
<span id="secondsTotal"
class="total-time">{{ substr($cachedData['total_seconds'], 0, 1) . ':' . substr($cachedData['seconds_elapsed'], 1, 2) }}</span>
@elseif($elementToShow === 'songImage')
<img id="artImage" class="spin"
style="position: absolute; left: 12%; top: 2%; border-radius:20px; z-index: 1" width="220px"
height="200px" src="{{ $cachedData['image'] }}" />
@elseif($elementToShow === 'audioURL')
<audio src="{{ $cachedData['audioURL'] }}" id="audio"></audio>
@elseif($elementToShow === 'spotifyURL')
<div class="spotify w-full">
<svg width="24" height="24" fill="green" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd"
clip-rule="evenodd">
<path
d="M19.098 10.638c-3.868-2.297-10.248-2.508-13.941-1.387-.593.18-1.22-.155-1.399-.748-.18-.593.154-1.22.748-1.4 4.239-1.287 11.285-1.038 15.738 1.605.533.317.708 1.005.392 1.538-.316.533-1.005.709-1.538.392zm-.126 3.403c-.272.44-.847.578-1.287.308-3.225-1.982-8.142-2.557-11.958-1.399-.494.15-1.017-.129-1.167-.623-.149-.495.13-1.016.624-1.167 4.358-1.322 9.776-.682 13.48 1.595.44.27.578.847.308 1.286zm-1.469 3.267c-.215.354-.676.465-1.028.249-2.818-1.722-6.365-2.111-10.542-1.157-.402.092-.803-.16-.895-.562-.092-.403.159-.804.562-.896 4.571-1.045 8.492-.595 11.655 1.338.353.215.464.676.248 1.028zm-5.503-17.308c-6.627 0-12 5.373-12 12 0 6.628 5.373 12 12 12 6.628 0 12-5.372 12-12 0-6.627-5.372-12-12-12z" />
</svg>
<a target="_blank"
href="{{ $cachedData['spotifyURL'] }}">{{ Str::limit($cachedData['spotifyURL'], 20, '...') }}</a>
</div>
@endif
@endif
</div>
This is kind of a solution, because wire:poll takes a “dynamic” value. But it crashes after a while and sometimes, not always, with a message of:
Maximum execution time of 60 seconds exceeded
I don’t have really more ideas for this. I tried in so many ways…
I tried to work with JavaScript but I cannot work with PHP variables and I cannot even do "setAttribute"
for the parent div with wire:poll.
I tried to use a Queue of laravel, but the component since it’s a Livewire component it kinda broke the code.
Someone just told me to try to use, WebSocket Broadcasting Laravel.
woweya is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.