You've already forked Atomcms-edit
265 lines
12 KiB
PHP
Executable File
265 lines
12 KiB
PHP
Executable File
@extends('layouts.app')
|
|
|
|
@section('title', __('radio.music') . ' - ' . config('app.name'))
|
|
|
|
@push('styles')
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/inter@4.x/400-700.css">
|
|
<style>
|
|
:root {
|
|
--radio-primary: {{ $primaryColor ?? '#eeb425' }};
|
|
--radio-secondary: {{ $secondaryColor ?? '#1a1a2e' }};
|
|
--radio-text: {{ $textColor ?? '#ffffff' }};
|
|
--radio-accent: {{ $accentColor ?? '#ff6b6b' }};
|
|
}
|
|
|
|
.radio-container {
|
|
background: linear-gradient(135deg, var(--radio-secondary) 0%, #0f0f1a 100%);
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.radio-player {
|
|
background: rgba(255, 255, 255, 0.05);
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.now-playing-card {
|
|
background: rgba(255, 255, 255, 0.08);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.stat-card {
|
|
background: rgba(255, 255, 255, 0.05);
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
}
|
|
|
|
.animate-pulse-slow {
|
|
animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.7; }
|
|
}
|
|
|
|
.live-badge {
|
|
background: linear-gradient(90deg, #ef4444, #f97316);
|
|
animation: livePulse 2s infinite;
|
|
}
|
|
|
|
@keyframes livePulse {
|
|
0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); }
|
|
50% { box-shadow: 0 0 0 8px rgba(239, 68, 68, 0); }
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
<div class="radio-container text-white">
|
|
@if(!$enabled)
|
|
<!-- Radio Disabled State -->
|
|
<div class="min-h-screen flex items-center justify-center p-6">
|
|
<div class="text-center max-w-md">
|
|
<div class="mb-6">
|
|
<svg class="w-24 h-24 mx-auto text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
|
</svg>
|
|
</div>
|
|
<h1 class="text-3xl font-bold mb-4">{{ __('radio.offline') }}</h1>
|
|
<p class="text-gray-400 mb-6">{{ __('radio.offline_message') }}</p>
|
|
@if($offlineMessage)
|
|
<div class="bg-gray-800 rounded-lg p-4">
|
|
<p class="text-gray-300">{{ $offlineMessage }}</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@else
|
|
<!-- Radio Player Section -->
|
|
<div class="max-w-6xl mx-auto px-4 py-8">
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between mb-8">
|
|
<div>
|
|
<h1 class="text-3xl font-bold flex items-center gap-3">
|
|
<span class="live-badge inline-flex items-center px-3 py-1 rounded-full text-xs font-bold">
|
|
<span class="w-2 h-2 bg-white rounded-full mr-2"></span>
|
|
{{ __('radio.live') }}
|
|
</span>
|
|
{{ __('radio.music') }}
|
|
</h1>
|
|
<p class="text-gray-400 mt-1">{{ __('radio.music_desc') }}</p>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="flex gap-6">
|
|
@if($showListeners)
|
|
<div class="stat-card rounded-lg px-4 py-2 flex items-center gap-2">
|
|
<svg class="w-5 h-5 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
</svg>
|
|
<span id="listenerCount" class="font-semibold">--</span>
|
|
<span class="text-gray-400 text-sm">{{ __('radio.listeners') }}</span>
|
|
</div>
|
|
@endif
|
|
<a href="{{ route('radio.leaderboard') }}" class="stat-card rounded-lg px-4 py-2 flex items-center gap-2 hover:bg-white/10 transition">
|
|
<svg class="w-5 h-5 text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
|
</svg>
|
|
<span class="font-semibold">{{ __('radio.top_100') }}</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Player -->
|
|
<div class="radio-player rounded-2xl p-6 mb-8">
|
|
<div class="grid md:grid-cols-3 gap-6 items-center">
|
|
<!-- Album Art / DJ Avatar -->
|
|
<div class="flex justify-center">
|
|
<div id="djAvatar" class="w-32 h-32 rounded-full bg-gradient-to-br from-amber-400 to-amber-600 flex items-center justify-center text-4xl">
|
|
@if($currentDJ && $currentDJ->user)
|
|
<img src="{{ $currentDJ->user->avatar_url ?? asset('images/default-avatar.png') }}" alt="{{ $currentDJ->user->username }}" class="w-full h-full rounded-full object-cover">
|
|
@else
|
|
🎵
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Track Info -->
|
|
<div class="text-center">
|
|
<div id="nowPlayingInfo">
|
|
<p class="text-gray-400 text-sm mb-1">{{ __('radio.now_playing') }}</p>
|
|
<h2 id="trackTitle" class="text-xl font-bold mb-2">
|
|
@if($nowPlaying && $nowPlaying['title'])
|
|
{{ $nowPlaying['title'] }}
|
|
@else
|
|
--
|
|
@endif
|
|
</h2>
|
|
@if($nowPlaying && $nowPlaying['artist'])
|
|
<p id="trackArtist" class="text-gray-400">{{ $nowPlaying['artist'] }}</p>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- DJ Info -->
|
|
@if($showCurrentDJ && $currentDJ && $currentDJ->user)
|
|
<div class="mt-4 pt-4 border-t border-white/10">
|
|
<p class="text-gray-400 text-sm mb-1">{{ __('radio.host') }}</p>
|
|
<p class="font-semibold text-amber-400">{{ $currentDJ->user->username }}</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
</div>
|
|
|
|
@push('scripts')
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const player = document.getElementById('radioPlayer');
|
|
const playBtn = document.getElementById('playPauseBtn');
|
|
const playIcon = document.getElementById('playIcon');
|
|
const pauseIcon = document.getElementById('pauseIcon');
|
|
const volumeSlider = document.getElementById('volumeSlider');
|
|
const listenerCount = document.getElementById('listenerCount');
|
|
|
|
// Play/Pause
|
|
playBtn.addEventListener('click', function() {
|
|
if (player.paused) {
|
|
player.play().catch(function(e) {
|
|
console.log('Autoplay blocked:', e);
|
|
});
|
|
playIcon.classList.add('hidden');
|
|
pauseIcon.classList.remove('hidden');
|
|
} else {
|
|
player.pause();
|
|
playIcon.classList.remove('hidden');
|
|
pauseIcon.classList.add('hidden');
|
|
}
|
|
});
|
|
|
|
// Volume
|
|
volumeSlider.addEventListener('input', function() {
|
|
player.volume = this.value / 100;
|
|
});
|
|
|
|
// Initial volume
|
|
player.volume = volumeSlider.value / 100;
|
|
|
|
// Update listeners count
|
|
function updateListeners() {
|
|
fetch('/api/radio/listeners')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (listenerCount) {
|
|
listenerCount.textContent = data.count.toLocaleString();
|
|
}
|
|
})
|
|
.catch(() => {
|
|
if (listenerCount) listenerCount.textContent = '--';
|
|
});
|
|
}
|
|
|
|
// Update now playing
|
|
function updateNowPlaying() {
|
|
fetch('/api/radio/now-playing')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
const titleEl = document.getElementById('trackTitle');
|
|
const artistEl = document.getElementById('trackArtist');
|
|
if (titleEl && data.title) titleEl.textContent = data.title;
|
|
if (artistEl && data.artist) artistEl.textContent = data.artist;
|
|
})
|
|
.catch(() => {});
|
|
}
|
|
|
|
// Auto-update listeners every 30 seconds
|
|
updateListeners();
|
|
setInterval(updateListeners, 30000);
|
|
|
|
// Auto-update now playing every 15 seconds
|
|
updateNowPlaying();
|
|
setInterval(updateNowPlaying, 15000);
|
|
|
|
// Load user points if logged in
|
|
if (document.body.classList.contains('authenticated')) {
|
|
loadUserPoints();
|
|
setInterval(loadUserPoints, 60000); // Update every minute
|
|
}
|
|
});
|
|
|
|
function loadUserPoints() {
|
|
fetch('/api/radio/points/user')
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Not authenticated');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
const pointsCount = document.getElementById('userPointsCount');
|
|
const userRank = document.getElementById('userRank');
|
|
const pointsHistory = document.getElementById('pointsHistory');
|
|
|
|
if (pointsCount) pointsCount.textContent = data.points || 0;
|
|
if (userRank) userRank.textContent = data.rank || '--';
|
|
|
|
if (pointsHistory && data.history && data.history.length > 0) {
|
|
pointsHistory.innerHTML = data.history.map(item => `
|
|
<div class="flex justify-between items-center py-1 border-b border-gray-700 text-xs">
|
|
<span class="text-gray-300">${item.reason}</span>
|
|
<span class="${item.points > 0 ? 'text-green-400' : 'text-red-400'}">
|
|
${item.points > 0 ? '+' : ''}${item.points}
|
|
</span>
|
|
</div>
|
|
`).join('');
|
|
} else if (pointsHistory) {
|
|
pointsHistory.innerHTML = '<p class="text-xs text-gray-500">{{ __("radio.no_history") }}</p>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.log('User points not available:', error.message);
|
|
// Silently fail if not authenticated or other error
|
|
});
|
|
}
|
|
</script>
|
|
@endpush
|