38018-vm/index.php
2026-01-31 15:07:49 +00:00

842 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
require_once __DIR__ . '/db/config.php';
@ini_set('display_errors', '0');
@error_reporting(E_ALL);
session_start();
// Simple admin check: access via index.php?admin=lili
if (isset($_GET['admin']) && $_GET['admin'] === 'lili') {
$_SESSION['is_admin'] = true;
}
if (isset($_GET['logout'])) {
unset($_SESSION['is_admin']);
}
$isAdmin = !empty($_SESSION['is_admin']);
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Lili Records Radio - La mejor música en vivo.';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
$streamUrl = "https://play.radioking.io/lili-record-s-radio";
$whatsappNumber = "5359177041";
$whatsappLink = "https://wa.me/" . $whatsappNumber;
$youtubeUrl = "https://www.youtube.com/@lilirecords";
$promoImage = "assets/pasted-20260130-234122-115a4b49.png";
$qrImage = "assets/pasted-20260131-000858-4fff58f0.jpg";
$logoImage = "assets/pasted-20260131-002028-7985dfae.png";
// Fetch latest requests and songs
$requests = [];
$songs = [];
$activeSong = null;
try {
$stmt = db()->query("SELECT name, phone, message, created_at FROM listener_requests ORDER BY created_at DESC LIMIT 20");
$requests = $stmt->fetchAll();
$stmtSongs = db()->query("SELECT * FROM songs ORDER BY created_at ASC");
$songs = $stmtSongs->fetchAll();
foreach ($songs as $s) {
if ($s["is_active"]) {
$activeSong = $s;
break;
}
}
} catch (Exception $e) {
// Silently fail if table doesn't exist yet or other DB error
}
?>
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Lili Records Radio</title>
<?php if ($projectDescription): ?>
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>" />
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
:root {
--primary-color: #ff2d55;
--glass-bg: rgba(0, 0, 0, 0.7);
--glass-border: rgba(255, 255, 255, 0.15);
}
body {
font-family: 'Inter', sans-serif;
background: #050505;
color: #fff;
min-height: 100vh;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow-x: hidden;
}
/* Background Image */
.bg-image {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('<?= $promoImage ?>') center/cover no-repeat;
filter: brightness(0.25) blur(8px);
z-index: -1;
}
.main-wrapper {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: center;
width: 100%;
max-width: 1200px;
padding: 2rem;
gap: 2rem;
z-index: 1;
}
@media (max-width: 992px) {
.main-wrapper {
flex-direction: column;
align-items: center;
padding: 1rem;
}
.left-column {
position: static;
flex: none;
width: 100%;
max-width: 500px;
}
body {
overflow-y: auto;
height: auto;
}
}
.left-column {
position: sticky;
top: 2rem;
flex: 0 0 320px;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.player-container {
background: var(--glass-bg);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 30px;
padding: 2rem;
width: 100%;
text-align: center;
box-shadow: 0 25px 50px rgba(0,0,0,0.8);
position: relative;
overflow: hidden;
}
.right-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 2rem;
max-width: 700px;
width: 100%;
min-width: 0;
}
.promo-image {
width: 100%;
height: auto;
border-radius: 35px;
box-shadow: 0 35px 70px rgba(0,0,0,0.8);
border: 1px solid var(--glass-border);
}
.comments-window {
background: var(--glass-bg);
backdrop-filter: blur(25px);
-webkit-backdrop-filter: blur(25px);
border: 1px solid var(--glass-border);
border-radius: 35px;
padding: 2rem;
box-shadow: 0 35px 70px rgba(0,0,0,0.6);
display: flex;
flex-direction: column;
min-height: 450px;
}
.comments-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--glass-border);
}
.comments-list {
overflow-y: auto;
flex: 1;
padding-right: 10px;
max-height: 550px;
}
.comment-item {
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 20px;
padding: 1.5rem;
margin-bottom: 1.2rem;
transition: all 0.3s ease;
position: relative;
}
.comment-item:hover {
background: rgba(255,255,255,0.1);
transform: scale(1.02);
}
.comment-user {
font-weight: 700;
color: var(--primary-color);
display: block;
margin-bottom: 0.4rem;
}
.btn-wa-reply {
background: #25d366;
color: #fff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba(37, 211, 102, 0.4);
text-decoration: none;
}
.btn-wa-reply:hover {
transform: scale(1.2) rotate(15deg);
color: #fff;
}
.form-control-glass {
background: #ffffff;
border: none;
color: #000;
border-radius: 18px;
padding: 0.9rem 1.4rem;
font-weight: 500;
}
.btn-send-request {
background: linear-gradient(45deg, var(--primary-color), #ff512f);
border: none;
border-radius: 18px;
font-weight: 700;
box-shadow: 0 10px 25px rgba(255, 45, 85, 0.4);
}
.radio-logo {
width: 85px;
height: 85px;
border-radius: 50%;
margin: 0 auto 1.2rem;
box-shadow: 0 0 30px rgba(255, 45, 85, 0.6);
animation: pulse 3s infinite;
overflow: hidden;
border: 4px solid rgba(255,255,255,0.25);
position: relative;
z-index: 2;
}
.radio-logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.btn-play {
width: 60px;
height: 60px;
border-radius: 50%;
background: #fff;
color: #000;
border: none;
font-size: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
position: relative;
z-index: 2;
}
.btn-play:hover {
background: var(--primary-color);
color: #fff;
transform: scale(1.15);
}
.admin-stats-box {
background: var(--glass-bg);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 25px;
padding: 1.5rem;
text-align: center;
box-shadow: 0 15px 30px rgba(0,0,0,0.5);
}
.stat-widget-container {
background: rgba(255,255,255,0.04);
border-radius: 15px;
padding: 0.8rem;
border: 1px solid rgba(255,255,255,0.05);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.whatsapp-btn {
position: fixed;
bottom: 30px;
left: 30px;
width: 70px;
height: 70px;
background: #25d366;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5rem;
box-shadow: 0 15px 35px rgba(37, 211, 102, 0.5);
text-decoration: none;
z-index: 1000;
animation: bounce 2.5s infinite;
}
.btn-youtube-float {
position: fixed;
bottom: 115px;
left: 30px;
width: 70px;
height: 70px;
background: #ff0000;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.2rem;
box-shadow: 0 15px 35px rgba(255, 0, 0, 0.5);
text-decoration: none;
z-index: 1000;
animation: bounce 2.8s infinite;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {transform: translateY(0);}
40% {transform: translateY(-15px);}
60% {transform: translateY(-8px);}
}
.live-badge {
position: absolute;
top: 20px;
left: 20px;
background: var(--primary-color);
padding: 5px 12px;
border-radius: 20px;
font-size: 0.7rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 2px;
box-shadow: 0 5px 15px rgba(255, 45, 85, 0.5);
z-index: 3;
}
/* Visualizer Canvas */
#visualizer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 140px;
z-index: 1;
opacity: 0.8;
pointer-events: none;
}
.song-info {
position: relative;
z-index: 2;
}
.player-controls-row {
position: relative;
z-index: 2;
}
.qr-container {
cursor: pointer;
transition: all 0.3s ease;
}
.qr-container:hover {
transform: scale(1.1);
}
/* Song Management Styles */
.song-item-modal {
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 20px;
padding: 1rem 1.5rem;
margin-bottom: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.3s ease;
}
.song-item-modal:hover {
background: rgba(255,255,255,0.1);
}
.song-active-modal {
border-color: var(--primary-color);
background: rgba(255, 45, 85, 0.1);
}
.song-title-text {
font-weight: 700;
display: block;
color: #fff;
}
.song-artist-text {
font-size: 0.8rem;
opacity: 0.6;
color: #fff;
}
.btn-toggle-song {
background: rgba(255,255,255,0.1);
color: #fff;
border: none;
border-radius: 12px;
padding: 5px 15px;
font-size: 0.8rem;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-toggle-song.active {
background: var(--primary-color);
}
.btn-delete-song {
background: rgba(255,0,0,0.1);
color: #ff4444;
border: none;
border-radius: 50%;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.btn-delete-song:hover {
background: #ff4444;
color: #fff;
}
</style>
</head>
<body>
<div class="bg-image"></div>
<div class="main-wrapper">
<!-- Left: Player -->
<div class="left-column">
<aside class="player-container">
<div class="live-badge">En Vivo</div>
<div class="radio-logo">
<img src="<?= $logoImage ?>" alt="Lili Records Logo">
</div>
<div class="song-info mb-4">
<span class="d-block fw-bold fs-5" id="songTitle"><?= ($activeSong ? htmlspecialchars($activeSong["title"]) : "Conectando...") ?></span>
<span class="d-block opacity-50 small" id="artistName"><?= ($activeSong ? htmlspecialchars($activeSong["artist"]) : "Lili Records Radio") ?></span>
</div>
<div class="player-controls-row d-flex align-items-center gap-3 justify-content-center">
<button class="btn-play" id="playBtn">
<i class="fas fa-play" id="playIcon"></i>
</button>
<div class="d-flex align-items-center gap-2" style="width: 120px;">
<i class="fas fa-volume-up opacity-50"></i>
<input type="range" class="form-range" id="volumeSlider" min="0" max="1" step="0.01" value="0.8">
</div>
</div>
<canvas id="visualizer"></canvas>
<div class="mt-4 pt-3 border-top border-white border-opacity-10 position-relative" style="z-index: 2;">
<?php if ($isAdmin): ?>
<a href="#playlistSection" class="btn btn-sm btn-outline-light rounded-pill px-4 mb-2 w-100">
<i class="fas fa-list-ul me-2"></i> Gestión de Playlist
</a>
<div class="mt-2 small text-primary fw-bold"><i class="fas fa-user-shield me-1"></i> PANEL ADMINISTRADOR</div>
<a href="?logout=1" class="btn btn-sm btn-outline-danger px-4 rounded-pill mt-2 w-100">Cerrar Sesión</a>
<?php endif; ?>
</div>
</aside>
<?php if ($isAdmin): ?>
<div class="admin-stats-box">
<h6 class="small mb-3 fw-bold text-uppercase letter-spacing-1">
<i class="fas fa-users me-2 text-primary"></i>
Oyentes Online
</h6>
<div class="stat-widget-container">
<a href="https://whos.amung.us/stats/lili-records/" target="_blank">
<img src="https://whos.amung.us/widget/lili-records.png" alt="Contador de usuarios" border="0">
</a>
</div>
<p class="small opacity-50 mt-3 mb-0" style="font-size: 0.7rem;">Estadísticas en tiempo real</p>
</div>
<?php endif; ?>
</div>
<!-- Right: Image + Content -->
<div class="right-content">
<img src="<?= $promoImage ?>" alt="Lili Records Promo" class="promo-image">
<?php if ($isAdmin): ?>
<!-- Playlist Window (Admin Only) -->
<div class="comments-window mb-4" id="playlistSection" style="min-height: auto;">
<div class="comments-header border-0 mb-3">
<div>
<h5 class="mb-1"><i class="fas fa-list-ul me-2 text-primary"></i>Gestión de Playlist</h5>
<p class="small opacity-60 mb-0">Control total de canciones para la radio.</p>
</div>
<button class="btn btn-sm btn-outline-primary rounded-pill px-3" onclick="document.getElementById('addSongForm').classList.toggle('d-none')">
<i class="fas fa-plus me-1"></i> Añadir
</button>
</div>
<div id="addSongForm" class="mb-4 d-none p-3 rounded-4" style="background: rgba(255,255,255,0.05); border: 1px solid var(--glass-border);">
<form action="manage_songs.php" method="POST" class="row g-2">
<input type="hidden" name="action" value="add">
<div class="col-sm-5">
<input type="text" name="title" class="form-control form-control-glass" placeholder="Título" required>
</div>
<div class="col-sm-5">
<input type="text" name="artist" class="form-control form-control-glass" placeholder="Artista" required>
</div>
<div class="col-sm-2">
<button type="submit" class="btn btn-send-request text-white w-100 h-100">
<i class="fas fa-plus"></i>
</button>
</div>
</form>
</div>
<div class="comments-list" style="max-height: 400px; flex: none;">
<?php if (empty($songs)): ?>
<div class="text-center py-4 opacity-30">
<i class="fas fa-music fa-3x mb-3"></i>
<p>No hay canciones en la lista.</p>
</div>
<?php else: ?>
<?php foreach ($songs as $song): ?>
<div class="song-item-modal <?= $song['is_active'] ? 'song-active-modal' : '' ?> p-3 mb-2 d-flex justify-content-between align-items-center">
<div>
<span class="song-title-text"><?= htmlspecialchars($song['title']) ?></span>
<span class="song-artist-text"><?= htmlspecialchars($song['artist']) ?></span>
</div>
<div class="d-flex align-items-center gap-2">
<form action="manage_songs.php" method="POST" class="m-0">
<input type="hidden" name="action" value="toggle_active">
<input type="hidden" name="id" value="<?= $song['id'] ?>">
<button type="submit" class="btn-toggle-song <?= $song['is_active'] ? 'active' : '' ?> py-1 px-3">
<?= $song['is_active'] ? 'AL AIRE' : 'PONER AL AIRE' ?>
</button>
</form>
<form action="manage_songs.php" method="POST" class="m-0" onsubmit="return confirm('¿Eliminar canción?')">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= $song['id'] ?>">
<button type="submit" class="btn-delete-song" style="width: 28px; height: 28px;">
<i class="fas fa-trash-alt" style="font-size: 0.7rem;"></i>
</button>
</form>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<!-- Messages Window -->
<div class="comments-window">
<div class="comments-header border-0 mb-3">
<div>
<h5 class="mb-1"><i class="fas fa-paper-plane me-2 text-primary"></i><?= $isAdmin ? 'Mensajes de Oyentes' : '¡Envía tu mensaje!' ?></h5>
<p class="small opacity-60 mb-0"><?= $isAdmin ? 'Peticiones en tiempo real' : 'Pide una canción o envía un saludo al aire.' ?></p>
</div>
<div class="text-center qr-container" style="width: 90px;" data-bs-toggle="modal" data-bs-target="#qrModal">
<img src="<?= $qrImage ?>" alt="QR Pago" class="w-100 rounded-3 mb-1">
<p class="small fw-bold mb-0"><?= $isAdmin ? 'QR' : 'APOYAR' ?></p>
</div>
</div>
<div class="comments-list" id="commentsList">
<?php if (empty($requests)): ?>
<div class="text-center py-5 opacity-30">
<i class="fas fa-comment-slash fa-4x mb-3"></i>
<p>No hay mensajes todavía.</p>
</div>
<?php else: ?>
<?php foreach ($requests as $req): ?>
<div class="comment-item">
<span class="comment-user"><?= htmlspecialchars($req['name']) ?></span>
<p class="mb-2"><?= htmlspecialchars($req['message']) ?></p>
<div class="d-flex justify-content-between align-items-center">
<span class="small opacity-40"><?= date('d M, H:i', strtotime($req['created_at'])) ?></span>
<?php if ($isAdmin && !empty($req['phone'])): ?>
<a href="https://wa.me/<?= preg_replace('/[^0-9]/', '', $req['phone']) ?>" class="btn-wa-reply" target="_blank">
<i class="fab fa-whatsapp"></i>
</a>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<form action="submit_request.php" method="POST" class="request-form mt-auto pt-4">
<div class="row g-2 mb-2">
<div class="col-sm-6">
<input type="text" name="name" class="form-control glass-input" placeholder="Tu nombre" required style="background: #fff; border: none; color: #000; border-radius: 18px; padding: 0.9rem 1.4rem; font-weight: 500; width: 100%;">
</div>
<div class="col-sm-6">
<input type="text" name="phone" class="form-control glass-input" placeholder="Tu WhatsApp" style="background: #fff; border: none; color: #000; border-radius: 18px; padding: 0.9rem 1.4rem; font-weight: 500; width: 100%;">
</div>
</div>
<div class="input-group">
<textarea name="message" class="form-control glass-input" rows="1" placeholder="Mensaje o canción..." required style="background: #fff; border: none; color: #000; border-radius: 18px; padding: 0.9rem 1.4rem; font-weight: 500;"></textarea>
<button type="submit" class="btn btn-send-request text-white px-4">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<div class="text-center mt-3">
<a href="#" class="text-white opacity-40 small text-decoration-none" data-bs-toggle="modal" data-bs-target="#policyModal">Ver Políticas de Privacidad</a>
</div>
</form>
</div>
<div class="mt-4 text-center pb-5">
<p class="small text-white opacity-30 mb-0">&copy; 2026 Lili Record´s Radio. Música que conecta.</p>
</div>
</div>
</div>
<!-- Modal Política de Privacidad -->
<div class="modal fade" id="policyModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content border-0" style="background: rgba(15,15,15,0.98); border-radius: 35px; backdrop-filter: blur(30px);">
<div class="modal-header border-white border-opacity-10 px-5 py-4">
<h5 class="modal-title fw-bold">POLÍTICA DE PRIVACIDAD</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body px-5 py-4" style="font-size: 1rem; line-height: 1.8; opacity: 0.8;">
<ul class="list-unstyled">
<li class="mb-3 d-flex gap-3"><i class="fas fa-check text-primary mt-1"></i> Respeto absoluto: No se permiten comentarios ofensivos o discriminatorios.</li>
<li class="mb-3 d-flex gap-3"><i class="fas fa-check text-primary mt-1"></i> Neutralidad: No aceptamos contenidos políticos ni religiosos.</li>
<li class="mb-3 d-flex gap-3"><i class="fas fa-check text-primary mt-1"></i> Privacidad: Tus datos (WhatsApp/Nombre) solo se usan para identificarte en el aire.</li>
<li class="mb-3 d-flex gap-3"><i class="fas fa-check text-primary mt-1"></i> Seguridad: No compartimos tu información con terceros.</li>
</ul>
</div>
<div class="modal-footer border-0 px-5 pb-5">
<button type="button" class="btn btn-primary px-5 py-2 rounded-pill fw-bold" data-bs-dismiss="modal">ACEPTAR</button>
</div>
</div>
</div>
</div>
<!-- Modal QR Ampliado -->
<div class="modal fade" id="qrModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content bg-transparent border-0">
<div class="modal-body p-0 text-center">
<img src="<?= $qrImage ?>" alt="QR Pago Ampliado" class="img-fluid rounded-4 shadow-lg" style="max-width: 90vw; max-height: 80vh; border: 5px solid rgba(255,255,255,0.1);">
<div class="mt-4">
<button type="button" class="btn btn-light rounded-pill px-5 fw-bold" data-bs-dismiss="modal">CERRAR</button>
</div>
</div>
</div>
</div>
</div>
<a href="<?= $youtubeUrl ?>" target="_blank" class="btn-youtube-float" title="YouTube">
<i class="fab fa-youtube"></i>
</a>
<a href="track_click.php?type=whatsapp_open&redirect=<?= urlencode($whatsappLink) ?>" class="whatsapp-btn" title="WhatsApp">
<i class="fab fa-whatsapp"></i>
</a>
<audio id="audioPlayer" src="<?= $streamUrl ?>" crossorigin="anonymous"></audio>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
const audio = document.getElementById('audioPlayer');
const playBtn = document.getElementById('playBtn');
const playIcon = document.getElementById('playIcon');
const volumeSlider = document.getElementById('volumeSlider');
const songTitle = document.getElementById('songTitle');
const artistName = document.getElementById('artistName');
const canvas = document.getElementById('visualizer');
const ctx = canvas.getContext('2d');
const hasActiveSong = <?= json_encode(!empty($activeSong)) ?>;
let isPlaying = false;
let audioContext;
let analyser;
let source;
let dataArray;
let animationId;
function initVisualizer() {
if (!audioContext) {
try {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioContext.createAnalyser();
source = audioContext.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(audioContext.destination);
analyser.fftSize = 64;
const bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
} catch (e) {
console.warn("Visualizer failed to init:", e);
}
}
}
function draw() {
animationId = requestAnimationFrame(draw);
if (!analyser) return;
analyser.getByteFrequencyData(dataArray);
ctx.clearRect(0, 0, canvas.width, canvas.height);
const width = canvas.width;
const height = canvas.height;
const barCount = dataArray.length / 1.2;
const barWidth = (width / barCount);
let x = 0;
for (let i = 0; i < barCount; i++) {
let barHeight = (dataArray[i] / 255) * height * 0.8;
if (barHeight < 5) barHeight = 5;
const gradient = ctx.createLinearGradient(0, height - barHeight, 0, height);
gradient.addColorStop(0, '#ff2d55');
gradient.addColorStop(0.5, '#ff512f');
gradient.addColorStop(1, 'rgba(255, 45, 85, 0.2)');
ctx.fillStyle = gradient;
const bx = x + 2;
const bw = barWidth - 4;
const bh = barHeight;
const by = height - bh;
const radius = bw / 2;
ctx.beginPath();
ctx.roundRect(bx, by, bw, bh, [radius, radius, 0, 0]);
ctx.fill();
x += barWidth;
}
}
function resizeCanvas() {
canvas.width = canvas.parentElement.offsetWidth;
canvas.height = 140;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
playBtn.addEventListener('click', () => {
if (isPlaying) {
audio.pause();
playIcon.classList.replace('fa-pause', 'fa-play');
if (animationId) cancelAnimationFrame(animationId);
} else {
initVisualizer();
if (audioContext && audioContext.state === 'suspended') {
audioContext.resume();
}
audio.play().catch(e => console.error("Error playing audio:", e));
playIcon.classList.replace('fa-play', 'fa-pause');
draw();
}
isPlaying = !isPlaying;
});
volumeSlider.addEventListener('input', (e) => {
audio.volume = e.target.value;
});
async function fetchMetadata() {
if (hasActiveSong) return;
try {
const response = await fetch('https://api.radioking.io/widget/radio/lili-record-s-radio/track/current');
const data = await response.json();
if (data && data.title) {
songTitle.textContent = data.title;
artistName.textContent = data.artist || 'Lili Records Radio';
}
} catch (error) {
console.error('Error fetching metadata:', error);
}
}
setInterval(fetchMetadata, 10000);
fetchMetadata();
</script>
</body>
</html>