38428-vm/index.php
2026-02-16 21:12:06 +00:00

1771 lines
72 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/tracker.php';
track_visitor();
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Lili Records Radio - La mejor música en vivo.';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? 'assets/pasted-20260215-164611-6d2aee42.png';
// WhatsApp info
$whatsapp_link = "https://chat.whatsapp.com/DkG96pTzAFO3hvLqmzwmTY";
$whatsapp_number = '+5359177041';
$facebook_link = "https://www.facebook.com/profile.php?id=61587890927489";
?>
<!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>
<!-- Meta tags SEO -->
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>" />
<meta property="og:title" content="Lili Records Radio" />
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<meta property="twitter:card" content="summary_large_image" />
<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">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@1/index.js"></script>
<style>
:root {
--accent-color: #00c853; /* Refined Green */
--primary-color: #38bdf8; /* Vibrant Blue */
--bg-overlay: rgba(0, 0, 0, 0.15); /* Even lighter for maximum transparency */
--glass-bg: rgba(255, 255, 255, 0.03); /* Almost invisible glass */
--glass-border: rgba(255, 255, 255, 0.2);
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family: 'Inter', sans-serif;
color: #ffffff;
background-color: transparent;
}
.background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('assets/images/background.jpg') center/cover no-repeat;
z-index: -1;
transition: filter 0.05s ease-out, transform 0.05s ease-out;
will-change: filter, transform;
}
.background::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--bg-overlay);
}
.app-container {
display: flex;
flex-direction: column;
width: 100%;
min-height: 100vh;
align-items: center;
justify-content: center;
padding: 2rem 1.5rem;
box-sizing: border-box;
gap: 1.5rem;
}
/* Center Section: Player */
.player-section {
width: 100%;
max-width: 680px;
display: flex;
flex-direction: column;
justify-content: center;
z-index: 10;
}
.interaction-center {
width: 100%;
max-width: 520px;
z-index: 10;
}
.glass-card {
background: var(--glass-bg);
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px);
border: 2px solid transparent;
border-radius: 32px;
padding: 2.5rem;
box-shadow: 0 15px 45px rgba(0, 0, 0, 0.4),
0 0 30px var(--dynamic-glow-dim, transparent);
position: relative;
background-clip: padding-box;
overflow: hidden;
transition: border-radius 0.5s ease, background 0.5s ease, box-shadow 0.1s ease;
}
/* Animated colorful border */
.glass-card::before {
content: "";
position: absolute;
inset: 0;
border-radius: 32px;
padding: 2px; /* thickness */
background: var(--card-border-bg, linear-gradient(45deg, var(--dynamic-glow, #00e676), #38bdf8, var(--dynamic-glow-dim, #facc15), #f472b6, var(--dynamic-glow, #00e676)));
background-size: 400% 400%;
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
animation: gradient-border 8s linear infinite;
transition: background 0.05s ease;
}
@keyframes gradient-border {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.brand h1 {
font-size: 2.8rem;
font-weight: 800;
margin: 0 0 0.5rem;
background: linear-gradient(to right, #fff, var(--primary-color), var(--accent-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
letter-spacing: -1px;
filter: drop-shadow(0 0 10px rgba(56, 189, 248, 0.3));
}
.brand-logo {
width: 130px;
height: 130px;
border-radius: 50%;
object-fit: cover;
border: 4px solid var(--dynamic-glow, var(--primary-color));
box-shadow: 0 0 25px var(--dynamic-glow, rgba(56, 189, 248, 0.7)),
0 0 50px var(--dynamic-glow-dim, rgba(56, 189, 248, 0.4));
transition: transform 0.3s ease, border-color 0.1s ease, box-shadow 0.1s ease;
}
.logo-wrapper {
display: flex;
justify-content: center;
align-items: center;
gap: 2rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
}
.brand-logo:hover {
transform: scale(1.1) rotate(5deg);
}
.brand p {
font-size: 1rem;
opacity: 0.8;
margin-bottom: 2rem;
}
/* Radio Player UI */
.radio-player {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.now-playing {
display: flex;
align-items: center;
gap: 1.2rem;
background: rgba(255, 255, 255, 0.02);
padding: 1.2rem;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: inset 0 0 15px rgba(0,0,0,0.2);
position: relative;
overflow: hidden;
}
.track-cover-container {
width: 80px;
height: 80px;
border-radius: 12px;
overflow: hidden;
flex-shrink: 0;
position: relative;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
border: 2px solid rgba(255,255,255,0.1);
transition: transform 0.3s ease, box-shadow 0.1s ease;
}
.track-cover-container img {
width: 100%;
height: 100%;
object-fit: cover;
transition: opacity 0.5s ease;
}
.track-cover-container i {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.5rem;
color: rgba(255,255,255,0.5);
z-index: 1;
display: none;
}
.now-playing i {
font-size: 1.5rem;
color: var(--primary-color);
animation: pulse 2s infinite;
}
.track-info {
overflow: hidden;
flex: 1;
}
.track-title {
font-weight: 800;
font-size: 1.5rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: color 0.05s ease, transform 0.05s ease, text-shadow 0.05s ease, opacity 0.5s ease;
color: #ffffff;
text-shadow: 0 0 15px rgba(56, 189, 248, 0.6);
display: block;
margin-top: 0.2rem;
will-change: color, transform, text-shadow;
}
.track-label {
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--accent-color);
margin-bottom: -5px;
display: block;
}
@keyframes marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
.track-title.scrolling {
text-overflow: clip;
}
.track-title.scrolling span {
display: inline-block;
animation: marquee 15s linear infinite;
padding-right: 2rem;
}
.track-status {
font-size: 0.85rem;
opacity: 0.6;
}
.controls {
display: flex;
align-items: center;
gap: 1rem;
}
.play-btn {
width: 72px;
height: 72px;
border-radius: 50%;
background: linear-gradient(135deg, var(--primary-color), #0ea5e9);
border: none;
color: #fff;
font-size: 2rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
box-shadow: 0 0 20px rgba(56, 189, 248, 0.4), inset 0 0 10px rgba(255,255,255,0.2);
position: relative;
}
.play-btn::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
border: 2px solid var(--primary-color);
opacity: 0;
transition: all 0.4s;
}
.play-btn:hover::after {
opacity: 1;
transform: scale(1.2);
}
.play-btn.playing {
background: linear-gradient(135deg, #ff4444, #cc0000) !important;
box-shadow: 0 0 25px rgba(255, 68, 68, 0.6);
transform: scale(1.1);
animation: pulse-button 1.5s infinite;
}
@keyframes pulse-button {
0% { box-shadow: 0 0 0 0 rgba(255, 68, 68, 0.7); }
70% { box-shadow: 0 0 0 20px rgba(255, 68, 68, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 68, 68, 0); }
}
.now-playing {
display: flex;
align-items: center;
gap: 1rem;
background: rgba(255, 255, 255, 0.05);
padding: 1.2rem;
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: inset 0 0 15px rgba(0,0,0,0.2);
}
.play-btn:hover {
transform: scale(1.05);
background: #0ea5e9;
}
.visualizer-container {
width: 100%;
height: 120px;
margin-bottom: 1rem;
display: flex;
align-items: flex-end;
justify-content: center;
gap: 4px;
overflow: hidden;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
background: radial-gradient(circle at center, rgba(56, 189, 248, 0.05) 0%, transparent 70%);
position: relative;
padding: 0 10px;
}
.audio-visualizer {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 4px;
width: 100%;
height: 100%;
}
.visualizer-bar {
width: 18px;
height: 5px;
border-radius: 4px 4px 0 0;
background: var(--primary-color);
transition: height 0.05s ease;
min-height: 5px;
position: relative;
}
.visualizer-peak {
position: absolute;
width: 18px;
height: 12px;
background: linear-gradient(to bottom, #fff, transparent);
border-radius: 2px;
bottom: 0;
z-index: 2;
box-shadow: 0 -2px 10px rgba(255, 255, 255, 0.8);
pointer-events: none;
transition: background 0.1s ease;
}
@keyframes strobe-peak {
0%, 100% { opacity: 1; filter: brightness(1.5) contrast(1.2); }
50% { opacity: 0.3; filter: brightness(4) contrast(2); }
}
.strobe {
animation: strobe-peak 0.08s infinite steps(2);
box-shadow: 0 0 30px #fff, 0 0 50px var(--peak-glow, #fff) !important;
}
/* Colores vibrantes para las barras */
.visualizer-bar:nth-child(5n+1) { background: linear-gradient(to top, #00e676, #69f0ae); }
.visualizer-bar:nth-child(5n+2) { background: linear-gradient(to top, #38bdf8, #7dd3fc); }
.visualizer-bar:nth-child(5n+3) { background: linear-gradient(to top, #facc15, #fde047); }
.visualizer-bar:nth-child(5n+4) { background: linear-gradient(to top, #f472b6, #fb923c); }
.visualizer-bar:nth-child(5n+5) { background: linear-gradient(to top, #818cf8, #a5b4fc); }
/* Playing Animations */
body.is-playing .glass-card {
animation: card-pulse 4s infinite ease-in-out;
border-color: rgba(56, 189, 248, 0.5);
}
.featured-img-container {
width: 100%;
max-width: 900px;
position: relative;
border-radius: 32px;
overflow: hidden;
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.7);
aspect-ratio: 16 / 10;
transition: all 0.5s ease;
}
@keyframes card-pulse {
0%, 100% {
transform: translate(var(--shake-offset-x, 0), var(--shake-offset-y, 0)) scale(1);
box-shadow: 0 15px 45px rgba(0, 0, 0, 0.4), 0 0 20px var(--dynamic-glow-dim, rgba(0,0,0,0));
}
50% {
transform: translate(var(--shake-offset-x, 0), var(--shake-offset-y, 0)) scale(1.01);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5), 0 0 40px var(--dynamic-glow, rgba(56, 189, 248, 0.4));
}
}
@keyframes float-img {
0%, 100% { transform: translateY(0) scale(1); }
50% { transform: translateY(-15px) scale(1.02); }
}
.volume-slider {
flex: 1;
height: 6px;
-webkit-appearance: none;
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
outline: none;
}
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
background: #fff;
border-radius: 50%;
cursor: pointer;
}
/* Interaction Form */
.interaction-form {
margin-top: 2rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
text-align: left;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.8rem;
}
.interaction-form label {
font-size: 0.85rem;
font-weight: 700;
opacity: 0.9;
letter-spacing: 0.5px;
}
.interaction-form input,
.interaction-form textarea {
width: 100%;
padding: 0.8rem;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.2);
background-color: rgba(255, 255, 255, 0.05); /* Even more transparent */
color: #ffffff;
font-family: inherit;
font-size: 0.95rem;
box-sizing: border-box;
outline: none;
transition: all 0.2s;
backdrop-filter: blur(4px);
}
.interaction-form textarea {
resize: none;
height: 80px;
}
.interaction-form input::placeholder,
.interaction-form textarea::placeholder {
color: rgba(255, 255, 255, 0.6);
}
.send-whatsapp-btn {
background-color: var(--accent-color);
color: #fff;
border: none;
padding: 0.8rem;
border-radius: 12px;
font-weight: 700;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
transition: transform 0.2s, background 0.2s;
}
.send-whatsapp-btn:hover {
transform: translateY(-2px);
background-color: #00c853;
}
/* Center Section: Featured Image */
.image-section {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 5;
}
.featured-img-container {
width: 100%;
max-width: 900px;
position: relative;
border-radius: 32px;
overflow: hidden;
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.7);
aspect-ratio: 16 / 10;
}
.featured-img-container img {
width: 100%;
height: 100%;
object-fit: contain;
background: rgba(0,0,0,0.2);
transition: transform 0.5s;
}
.featured-img-container:hover img {
transform: scale(1.05);
}
/* Transfermovil QR Section */
.payment-section {
margin-top: 2rem;
text-align: center;
}
.qr-placeholder {
width: 220px;
height: 220px;
margin: 1rem auto;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #fff;
padding: 10px;
backdrop-filter: blur(4px);
cursor: pointer;
transition: all 0.3s ease;
animation: qr-pulse 2s infinite ease-in-out;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.qr-placeholder:hover {
animation-play-state: paused;
transform: scale(1.05);
box-shadow: 0 0 20px var(--accent-color);
background: rgba(255, 255, 255, 0.2);
}
@keyframes qr-pulse {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0, 230, 118, 0.4); }
50% { transform: scale(1.03); box-shadow: 0 0 20px 10px rgba(0, 230, 118, 0); }
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0, 230, 118, 0); }
}
/* Modal Styles */
.qr-modal {
display: none;
position: fixed;
z-index: 2000;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.6);
backdrop-filter: blur(15px);
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.qr-modal.show {
display: flex;
opacity: 1;
}
.qr-modal-content {
max-width: 90%;
max-height: 90%;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
padding: 30px;
border-radius: 32px;
position: relative;
transform: scale(0.7);
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
overflow: hidden;
box-shadow: 0 0 50px rgba(0, 230, 118, 0.4), 0 0 100px rgba(0, 0, 0, 0.5);
animation: glow-color-change 8s infinite ease-in-out;
border: 1px solid rgba(255, 255, 255, 0.2);
}
@keyframes glow-color-change {
0% { box-shadow: 0 0 50px rgba(0, 230, 118, 0.4), 0 0 100px rgba(0, 0, 0, 0.5); }
33% { box-shadow: 0 0 50px rgba(56, 189, 248, 0.4), 0 0 100px rgba(0, 0, 0, 0.5); }
66% { box-shadow: 0 0 50px rgba(244, 114, 182, 0.4), 0 0 100px rgba(0, 0, 0, 0.5); }
100% { box-shadow: 0 0 50px rgba(0, 230, 118, 0.4), 0 0 100px rgba(0, 0, 0, 0.5); }
}
.qr-modal-content::after {
content: "";
position: absolute;
top: -50%;
left: -150%;
width: 200%;
height: 200%;
background: linear-gradient(
120deg,
transparent,
rgba(255, 255, 255, 0.1),
transparent
);
transform: rotate(10deg);
pointer-events: none;
}
.qr-modal.show .qr-modal-content::after {
animation: modal-shine 4s infinite;
}
@keyframes modal-shine {
0% { left: -150%; }
20% { left: 150%; }
100% { left: 150%; }
}
.qr-modal.show .qr-modal-content {
transform: scale(1);
}
.qr-modal-close {
position: absolute;
top: 15px;
right: 20px;
color: #ffffff;
font-size: 2.5rem;
cursor: pointer;
z-index: 10;
text-shadow: 0 0 10px rgba(0,0,0,0.5);
}
.qr-placeholder i {
font-size: 3rem;
margin-bottom: 10px;
color: #fff;
}
.qr-placeholder span {
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
}
/* Floating Social Buttons */
.social-float-container {
position: fixed;
bottom: 2rem;
right: 2rem;
display: flex;
flex-direction: column;
gap: 1rem;
z-index: 100;
}
.social-float {
width: 60px;
height: 60px;
color: #FFF;
border-radius: 50%;
text-align: center;
font-size: 28px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
transition: all 0.05s ease-out; /* Super fast for vibration */
position: relative;
will-change: transform, background-color, box-shadow;
}
.social-float::before {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
padding: 3px;
background: linear-gradient(45deg, #00e676, #38bdf8, #facc15, #f472b6, #00e676);
background-size: 400% 400%;
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
animation: gradient-border 6s linear infinite;
}
.social-float:hover {
transform: scale(1.1) rotate(8deg);
color: #fff;
}
.social-float.whatsapp {
background-color: var(--accent-color);
box-shadow: 0 0 20px rgba(0, 230, 118, 0.5);
}
.social-float.youtube {
background-color: #ff0000;
box-shadow: 0 0 20px rgba(255, 0, 0, 0.5);
}
.social-float.facebook {
background-color: #1877F2;
box-shadow: 0 0 20px rgba(24, 119, 242, 0.5);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-5px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.7; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes alert-pulse {
0% { transform: scale(1); }
50% { transform: scale(1.3); background: #facc15 !important; }
100% { transform: scale(1); }
}
.counter-alert {
animation: alert-pulse 1s infinite ease-in-out;
box-shadow: 0 0 10px #facc15;
}
/* Responsive */
@media (max-width: 992px) {
.app-container {
flex-direction: column;
height: auto;
overflow-y: auto;
padding-top: 4rem;
}
.player-section, .interaction-center {
max-width: 100% !important;
margin-bottom: 2rem;
}
.interaction-center {
grid-template-columns: 1fr !important;
}
.chat-window, .ranking-window {
height: 400px !important;
}
.image-section {
padding-left: 0;
width: 100%;
}
.featured-img-container {
aspect-ratio: 1 / 1;
}
}
@media (max-width: 600px) {
.form-row {
grid-template-columns: 1fr;
}
.glass-card {
padding: 1.5rem;
}
.brand h1 {
font-size: 2rem;
}
}
</style>
</head>
<body>
<div class="background"></div>
<div class="app-container">
<!-- Left Section: Player -->
<section class="player-section">
<div class="glass-card">
<header class="brand">
<div class="logo-wrapper">
<img src="./assets/pasted-20260215-163754-def41f49.png" alt="Lili Records Logo" class="brand-logo">
<img src="./assets/pasted-20260215-171328-d90df4ce.jpg" alt="Logo Secundario" class="brand-logo">
</div>
<h1>Lili Records</h1>
<p>Siente la música, vive el ritmo.</p>
</header>
<div class="radio-player">
<div class="visualizer-container">
<div id="audio-visualizer" class="audio-visualizer"></div>
</div>
<div class="now-playing">
<div class="track-cover-container">
<i id="cover-placeholder" class="bi bi-broadcast"></i>
<img id="track-cover" src="./assets/pasted-20260215-163754-def41f49.png" alt="Cover" onerror="this.style.display='none'; document.getElementById('cover-placeholder').style.display='block';">
</div>
<div class="track-info">
<span class="track-label">ESTÁS ESCUCHANDO:</span>
<div id="track-title" class="track-title">Cargando stream...</div>
<div id="track-artist" class="track-artist" style="font-size: 0.95rem; font-weight: 600; opacity: 0.8; color: var(--primary-color); text-transform: uppercase; letter-spacing: 1px; margin-top: 2px;">Lili Records Radio</div>
<div class="track-status" style="margin-top: 5px; display: flex; align-items: center; gap: 5px; font-size: 0.75rem;">
<span style="width: 8px; height: 8px; background: #ff4444; border-radius: 50%; display: inline-block; animation: pulse 1.5s infinite;"></span>
EN VIVO
</div>
</div>
<button id="like-song-btn" onclick="likeSong()" style="background: none; border: none; color: #ff4444; font-size: 1.8rem; cursor: pointer; transition: transform 0.2s; display: flex; align-items: center; gap: 5px;">
<i class="bi bi-heart"></i>
<span id="like-count" style="font-size: 0.9rem; color: white; opacity: 0.8; font-weight: bold;"></span>
</button>
</div>
<div class="controls">
<button id="play-pause" class="play-btn" onclick="togglePlay()">
<i id="play-icon" class="bi bi-play-fill"></i>
</button>
<i class="bi bi-volume-up"></i>
<input type="range" class="volume-slider" min="0" max="1" step="0.01" value="1" oninput="changeVolume(this.value)">
</div>
<div id="recent-tracks-container" style="margin-top: 1rem; display: none;">
<span class="track-label" style="font-size: 0.65rem; margin-bottom: 0.5rem;">CANCIONES ANTERIORES:</span>
<div id="recent-tracks-list" style="display: flex; flex-direction: column; gap: 0.5rem;"></div>
</div>
<div id="top-songs-container" style="margin-top: 1.5rem; background: rgba(255, 255, 255, 0.03); padding: 1rem; border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.05);">
<span class="track-label" style="font-size: 0.65rem; margin-bottom: 0.8rem; color: #facc15;">
<i class="bi bi-trophy-fill"></i> TOP CANCIONES DE LA SEMANA
</span>
<div id="top-songs-list" style="display: flex; flex-direction: column; gap: 0.6rem;">
<div style="font-size: 0.8rem; opacity: 0.5; text-align: center;">Cargando ranking...</div>
</div>
</div>
<div class="interaction-form">
<div class="form-row">
<div class="form-group">
<label for="user-name">NOMBRE</label>
<input type="text" id="user-name" placeholder="Tu nombre..." oninput="this.style.borderColor='rgba(255, 255, 255, 0.2)'">
</div>
<div class="form-group">
<label for="user-phone">MOVIL</label>
<input type="tel" id="user-phone" placeholder="Tu número..." oninput="this.style.borderColor='rgba(255, 255, 255, 0.2)'" onchange="savePhone(this.value)">
</div>
</div>
<div class="form-group">
<label for="user-message">MENSAJE</label>
<textarea id="user-message" placeholder="¿Qué quieres escuchar?"></textarea>
</div>
<a href="<?= $whatsapp_link ?>" target="_blank" class="send-whatsapp-btn" style="text-decoration: none;">
<i class="bi bi-whatsapp"></i> WHATSAPP
</a>
</div>
<!-- Espacio para Código QR -->
<div class="payment-section">
<div class="qr-placeholder" style="padding: 0; overflow: hidden; background: rgba(255,255,255,0.1);" onclick="openQRModal()">
<img src="./assets/pasted-20260216-203652-f5645ae9.jpg" alt="Código QR" style="width: 100%; height: 100%; object-fit: contain; border-radius: 12px;">
</div>
<p style="color: var(--accent-color); font-size: 0.9rem; font-weight: 600; margin-top: 0.5rem; text-transform: uppercase;">
Contribuye con tu ayuda a nuestra Web App
</p>
</div>
</div>
</div>
<?php if (isset($_GET['admin']) && $_GET['admin'] === '1'): ?>
<!-- Admin Real-Time Stats (Only visible with ?admin=1) -->
<div class="glass-card mt-4" style="margin-top: 2rem;">
<h3 style="font-size: 1.2rem; margin-bottom: 1rem; color: var(--accent-color);">
<i class="bi bi-shield-lock"></i> Panel Admin Real-Time
</h3>
<iframe src="admin.php?token=lili_admin_2026" style="width: 100%; height: 400px; border: none; border-radius: 12px; background: rgba(0,0,0,0.2);"></iframe>
</div>
<?php endif; ?>
</section>
<!-- Interaction Center: Live Chat -->
<section class="interaction-center">
<!-- Live Web Chat -->
<div class="glass-card chat-window" style="height: 500px; display: flex; flex-direction: column;">
<h3 style="font-size: 1.2rem; margin-bottom: 1rem; color: var(--primary-color);">
<i class="bi bi-chat-dots-fill"></i> CHAT EN VIVO
</h3>
<div id="chat-messages" style="flex: 1; overflow-y: auto; margin-bottom: 1rem; padding-right: 5px; display: flex; flex-direction: column; gap: 0.8rem;">
<!-- Mensajes se cargarán aquí -->
<div style="opacity: 0.5; font-size: 0.9rem; text-align: center; margin-top: 2rem;">Cargando mensajes...</div>
</div>
<div class="chat-input-area" style="display: flex; gap: 0.5rem; position: relative;">
<input type="hidden" id="chat-user">
<button onclick="toggleEmojiPicker()" style="background: rgba(255,255,255,0.1); border: none; border-radius: 8px; color: white; padding: 0 0.5rem; cursor: pointer;">
<i class="bi bi-emoji-smile"></i>
</button>
<button id="camera-btn" onclick="document.getElementById('chat-file').click()" title="Subir foto" style="background: rgba(255,255,255,0.1); border: none; border-radius: 8px; color: white; padding: 0 0.5rem; cursor: pointer; position: relative; transition: all 0.3s;">
<i class="bi bi-camera-fill"></i>
<span id="photo-counter" style="position: absolute; top: -10px; right: -5px; background: var(--primary-color); color: white; font-size: 0.6rem; padding: 1px 4px; border-radius: 10px; font-weight: bold; border: 1px solid rgba(255,255,255,0.3); display: none;">0/5</span>
</button>
<div id="photo-limit-msg" style="display: none; position: absolute; bottom: -20px; left: 0; width: 100%; text-align: center; font-size: 0.65rem; color: #ff4444; font-weight: bold; animation: fadeIn 0.3s;"></div>
<input type="file" id="chat-file" style="display: none;" accept="image/*" onchange="uploadImage(this)">
<input type="text" id="chat-msg" placeholder="Escribe un mensaje..." style="flex: 1; font-size: 0.8rem; padding: 0.5rem; border-radius: 8px; border: none; background: rgba(255,255,255,0.05); color: white;">
<button onclick="sendChatMessage()" style="background: var(--primary-color); border: none; border-radius: 8px; color: white; padding: 0 1rem; cursor: pointer;">
<i class="bi bi-send-fill"></i>
</button>
<emoji-picker id="emoji-picker" style="display: none; position: absolute; bottom: 50px; left: 0; z-index: 1000; --num-columns: 6; --category-button-size: 1.5rem; width: 300px; height: 350px;"></emoji-picker>
</div>
</div>
</section>
</div>
<!-- Floating Social Links -->
<div class="social-float-container">
<a href="<?= $facebook_link ?>" class="social-float facebook" target="_blank" title="Facebook">
<i class="bi bi-facebook"></i>
</a>
<a href="https://www.youtube.com/@lilirecords" class="social-float youtube" target="_blank" title="YouTube">
<i class="bi bi-youtube"></i>
</a>
<a href="<?= $whatsapp_link ?>" class="social-float whatsapp" target="_blank" title="WhatsApp">
<i class="bi bi-whatsapp"></i>
</a>
</div>
<!-- QR Modal -->
<div id="qr-modal" class="qr-modal" onclick="closeQRModal()">
<div class="qr-modal-content" onclick="event.stopPropagation()">
<span class="qr-modal-close" onclick="closeQRModal()">&times;</span>
<img src="./assets/pasted-20260216-203652-f5645ae9.jpg" alt="QR Ampliado" style="width: 100%; max-width: 400px; height: auto; border-radius: 12px; display: block; margin: 0 auto;">
<p style="color: #333; text-align: center; margin-top: 1rem; font-weight: 700; font-size: 1.1rem;">¡ÚNETE A NUESTRO WHATSAPP!</p>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>
<audio id="radio-audio" src="https://listen.radioking.com/radio/828046/stream/897251" preload="auto" crossorigin="anonymous"></audio>
<audio id="welcome-sound" src="https://assets.mixkit.co/active_storage/sfx/2013/2013-preview.mp3" preload="auto"></audio>
<script>
const audio = document.getElementById('radio-audio');
const playBtn = document.getElementById('play-pause');
const playIcon = document.getElementById('play-icon');
const trackTitle = document.getElementById('track-title');
const trackArtist = document.getElementById('track-artist');
const trackCover = document.getElementById('track-cover');
const coverPlaceholder = document.getElementById('cover-placeholder');
const visualizerContainer = document.getElementById('audio-visualizer');
const glassCard = document.querySelector('.glass-card');
const trackLabel = document.querySelector('.track-label');
const socialIcons = document.querySelectorAll('.social-float');
let audioCtx;
let analyzer;
let source;
let animationId;
const BAR_COUNT = 16;
let visualizerBars = [];
let visualizerPeaks = [];
let barHeights = new Array(BAR_COUNT).fill(5);
let peakHeights = new Array(BAR_COUNT).fill(5);
const bg = document.querySelector('.background');
let colorOffset = 0;
function draw() {
if (audio.paused || audio.ended) {
animationId = null;
visualizerBars.forEach(bar => bar.style.height = '5px');
visualizerPeaks.forEach(peak => {
peak.style.bottom = '5%';
peak.style.background = 'linear-gradient(to bottom, #fff, transparent)';
peak.style.boxShadow = '0 -2px 10px rgba(255, 255, 255, 0.8)';
});
document.documentElement.style.setProperty('--dynamic-glow', 'rgba(56, 189, 248, 0.7)');
document.documentElement.style.setProperty('--dynamic-glow-dim', 'rgba(56, 189, 248, 0.4)');
if (bg) {
bg.style.filter = 'brightness(1)';
bg.style.transform = 'scale(1)';
}
return;
}
animationId = requestAnimationFrame(draw);
const bufferLength = analyzer.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
analyzer.getByteFrequencyData(dataArray);
colorOffset += 1;
// Bass detection for background flicker
// We use the first 4 bins (low frequencies)
const bassAvg = (dataArray[0] + dataArray[1] + dataArray[2] + dataArray[3]) / 4;
const bassIntensity = bassAvg / 255;
// Apply flicker to background
if (bg) {
const brightness = 1 + (bassIntensity * 0.85); // Ultra punchy
const scale = 1 + (bassIntensity * 0.06); // Stronger zoom
const saturation = 1 + (bassIntensity * 0.5); // Add color intensity
bg.style.filter = `brightness(${brightness}) saturate(${saturation})`;
bg.style.transform = `scale(${scale})`;
}
for (let i = 0; i < BAR_COUNT; i++) {
// Map frequency data to bars (skipping very low and very high)
const freqIndex = Math.floor((i / BAR_COUNT) * (dataArray.length * 0.7)) + 1;
const val = dataArray[freqIndex];
const targetHeight = (val / 255) * 100 + 5;
// Retro "slow fall" logic
if (targetHeight > barHeights[i]) {
barHeights[i] = targetHeight;
} else {
barHeights[i] -= 2;
if (barHeights[i] < 5) barHeights[i] = 5;
}
if (visualizerBars[i]) {
visualizerBars[i].style.height = `${barHeights[i]}%`;
}
// Peak logic
if (barHeights[i] > peakHeights[i]) {
peakHeights[i] = barHeights[i];
} else {
peakHeights[i] -= 1.2;
if (peakHeights[i] < 5) peakHeights[i] = 5;
}
if (visualizerPeaks[i]) {
visualizerPeaks[i].style.bottom = `${peakHeights[i]}%`;
const hue = Math.max(0, 200 - (peakHeights[i] * 1.8));
const color = `hsla(${hue}, 100%, 85%, 1)`;
const glow = `hsla(${hue}, 100%, 60%, 0.9)`;
visualizerPeaks[i].style.background = `linear-gradient(to bottom, ${color}, transparent)`;
visualizerPeaks[i].style.boxShadow = `0 -2px 12px ${glow}, 0 0 20px ${glow}`;
if (peakHeights[i] > 90) {
visualizerPeaks[i].classList.add('strobe');
visualizerPeaks[i].style.setProperty('--peak-glow', glow);
} else {
visualizerPeaks[i].classList.remove('strobe');
}
}
}
// Dynamic glow effect based on average volume
let sum = 0;
for(let i=0; i<dataArray.length; i++) sum += dataArray[i];
const average = sum / dataArray.length;
// Aggressive red glow and shake on high volume
if (average > 95) {
document.documentElement.style.setProperty('--dynamic-glow', 'rgba(255, 0, 0, 0.9)');
document.documentElement.style.setProperty('--dynamic-glow-dim', 'rgba(255, 0, 0, 0.5)');
document.documentElement.style.setProperty('--card-border-bg', '#ff0000');
// Physical vibration effect
const shakeIntensity = (average - 95) * 0.5;
document.documentElement.style.setProperty('--shake-offset-x', `${(Math.random() - 0.5) * shakeIntensity}px`);
document.documentElement.style.setProperty('--shake-offset-y', `${(Math.random() - 0.5) * shakeIntensity}px`);
// Intense scale boost for the card
const cardScale = 1 + (average / 255) * 0.04;
if (glassCard) glassCard.style.transform = `translate(var(--shake-offset-x), var(--shake-offset-y)) scale(${cardScale})`;
// Aggressive title effect
if (trackTitle) {
trackTitle.style.color = '#ff0000';
trackTitle.style.textShadow = '0 0 20px #ff0000, 0 0 40px #ff0000';
trackTitle.style.transform = `scale(${1 + (average / 255) * 0.12})`;
}
if (trackLabel) {
trackLabel.style.color = '#ff0000';
trackLabel.style.textShadow = '0 0 10px #ff0000';
}
// Social Icons aggression
socialIcons.forEach(icon => {
icon.style.backgroundColor = '#ff0000';
icon.style.boxShadow = '0 0 30px #ff0000, 0 0 60px rgba(255, 0, 0, 0.6)';
icon.style.transform = `translate(${(Math.random() - 0.5) * shakeIntensity * 2}px, ${(Math.random() - 0.5) * shakeIntensity * 2}px) scale(1.15)`;
});
} else {
const dominantHue = (average * 2 + colorOffset) % 360;
document.documentElement.style.setProperty('--dynamic-glow', `hsla(${dominantHue}, 100%, 60%, 0.8)`);
document.documentElement.style.setProperty('--dynamic-glow-dim', `hsla(${dominantHue}, 100%, 60%, 0.3)`);
document.documentElement.style.setProperty('--card-border-bg', '');
document.documentElement.style.setProperty('--shake-offset-x', '0px');
document.documentElement.style.setProperty('--shake-offset-y', '0px');
if (glassCard) glassCard.style.transform = '';
if (trackTitle) {
trackTitle.style.color = '';
trackTitle.style.textShadow = '';
trackTitle.style.transform = '';
}
if (trackLabel) {
trackLabel.style.color = '';
trackLabel.style.textShadow = '';
}
// Reset Social Icons
socialIcons.forEach(icon => {
icon.style.backgroundColor = '';
icon.style.boxShadow = '';
icon.style.transform = '';
});
}
}
function initVisualizer() {
// Create bars and peaks if not already created
if (visualizerBars.length === 0) {
visualizerContainer.innerHTML = '';
for (let i = 0; i < BAR_COUNT; i++) {
const barWrapper = document.createElement('div');
barWrapper.style.position = 'relative';
barWrapper.style.height = '100%';
barWrapper.style.display = 'flex';
barWrapper.style.alignItems = 'flex-end';
barWrapper.style.width = '18px';
const bar = document.createElement('div');
bar.className = 'visualizer-bar';
const peak = document.createElement('div');
peak.className = 'visualizer-peak';
barWrapper.appendChild(bar);
barWrapper.appendChild(peak);
visualizerContainer.appendChild(barWrapper);
visualizerBars.push(bar);
visualizerPeaks.push(peak);
}
}
if (!audioCtx) {
try {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
analyzer = audioCtx.createAnalyser();
source = audioCtx.createMediaElementSource(audio);
source.connect(analyzer);
analyzer.connect(audioCtx.destination);
analyzer.fftSize = 128;
} catch (e) {
console.error("AudioContext error:", e);
}
}
if (audioCtx && audioCtx.state === 'suspended') {
audioCtx.resume();
}
if (!animationId && !audio.paused) {
draw();
}
}
audio.addEventListener('play', () => {
initVisualizer();
});
function togglePlay() {
if (audio.paused) {
audio.play().then(() => {
initVisualizer();
}).catch(err => {
console.error("Error playing audio:", err);
});
playIcon.classList.remove('bi-play-fill');
playIcon.classList.add('bi-pause-fill');
playBtn.classList.add('playing');
document.body.classList.add('is-playing');
} else {
audio.pause();
playIcon.classList.remove('bi-pause-fill');
playIcon.classList.add('bi-play-fill');
playBtn.classList.remove('playing');
document.body.classList.remove('is-playing');
}
}
function changeVolume(val) {
audio.volume = val;
}
function savePhone(phone) {
const phoneInput = document.getElementById('user-phone');
const phoneRegex = /^\+?[0-9]{7,15}$/;
const cleanPhone = phone.replace(/\s/g, '');
if (cleanPhone && !phoneRegex.test(cleanPhone)) {
phoneInput.style.borderColor = '#ff4444';
return;
}
phoneInput.style.borderColor = 'rgba(255, 255, 255, 0.2)';
const formData = new FormData();
formData.append('phone', cleanPhone);
fetch('api/save_phone.php', {
method: 'POST',
body: formData
});
}
function sendToWhatsApp() {
const name = document.getElementById('user-name').value.trim();
const phone = document.getElementById('user-phone').value.trim();
const message = document.getElementById('user-message').value.trim();
const phoneRegex = /^\+?[0-9]{7,15}$/;
if (name.length < 3) {
alert('Por favor, ingresa un nombre válido (mínimo 3 caracteres).');
document.getElementById('user-name').style.borderColor = '#ff4444';
return;
}
if (!phone) {
alert('Por favor, ingresa tu número de móvil para continuar.');
document.getElementById('user-phone').style.borderColor = '#ff4444';
return;
}
if (!phoneRegex.test(phone.replace(/\s/g, ''))) {
alert('Por favor, ingresa un número de móvil válido (ej: +5359177041).');
document.getElementById('user-phone').style.borderColor = '#ff4444';
return;
}
const now = new Date();
const dateTime = now.toLocaleDateString() + ' ' + now.toLocaleTimeString();
const refId = Math.floor(1000 + Math.random() * 9000);
const currentSong = document.getElementById('track-title').innerText.replace(/\s{2,}/g, ' ').trim();
const text = `*PETICIÓN RADIO* (Ref: #${refId})\n\n` +
`*Nombre:* ${name}\n` +
`*Móvil:* ${phone}\n` +
`*Fecha:* ${dateTime}\n` +
`*Sonando ahora:* ${currentSong}\n\n` +
`*Mensaje:* ${message || 'Sin mensaje específico'}`;
const url = `https://wa.me/<?= str_replace('+', '', $whatsapp_number) ?>?text=${encodeURIComponent(text)}`;
window.open(url, '_blank');
}
// --- Chat Functionality ---
const chatMessages = document.getElementById('chat-messages');
const chatUser = document.getElementById('chat-user');
const chatMsg = document.getElementById('chat-msg');
const emojiPicker = document.getElementById('emoji-picker');
let lastMessageCount = 0;
function toggleEmojiPicker() {
emojiPicker.style.display = emojiPicker.style.display === 'none' ? 'block' : 'none';
}
emojiPicker.addEventListener('emoji-click', event => {
chatMsg.value += event.detail.unicode;
emojiPicker.style.display = 'none';
chatMsg.focus();
});
// Close emoji picker when clicking outside
document.addEventListener('click', (e) => {
if (!emojiPicker.contains(e.target) && !e.target.closest('button[onclick="toggleEmojiPicker()"]')) {
emojiPicker.style.display = 'none';
}
});
async function uploadImage(input) {
if (!input.files || !input.files[0]) return;
const file = input.files[0];
const formData = new FormData();
formData.append('image', file);
try {
const response = await fetch('api/upload.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
sendChatMessage(result.url, 'image');
updatePhotoCounter();
} else {
alert(result.error || 'Error al subir la imagen');
}
} catch (error) {
console.error('Upload error:', error);
alert('Error de conexión al subir la imagen');
}
input.value = ''; // Reset input
}
async function fetchMessages() {
try {
const response = await fetch('api/chat.php');
const messages = await response.json();
const currentUserName = document.getElementById('user-name').value.trim() || 'Anónimo';
chatMessages.innerHTML = '';
messages.forEach(msg => {
const div = document.createElement('div');
const isLike = msg.message.includes('❤️');
const customColor = msg.custom_color ? `color: ${msg.custom_color} !important;` : '';
div.style.background = isLike ? 'rgba(255, 68, 68, 0.15)' : 'rgba(255,255,255,0.05)';
div.style.padding = '0.8rem';
div.style.borderRadius = '12px';
div.style.borderLeft = `4px solid ${isLike ? '#ff4444' : (msg.username === currentUserName ? 'var(--primary-color)' : 'rgba(255,255,255,0.2)')}`;
let content = '';
if (msg.type === 'image') {
content = `<img src="${msg.message}" style="max-width: 100%; border-radius: 8px; margin-top: 5px; cursor: pointer;" onclick="window.open('${msg.message}', '_blank')">`;
} else {
content = `<div style="font-size: 0.85rem; line-height: 1.4; ${isLike ? 'font-weight: 600;' : ''}">${msg.message}</div>`;
}
div.innerHTML = `
<div style="font-size: 0.7rem; font-weight: bold; ${customColor || (isLike ? 'color: #ff4444' : 'var(--primary-color)')}; margin-bottom: 2px;">
${msg.username}
</div>
${content}
<div style="font-size: 0.6rem; opacity: 0.4; margin-top: 4px; text-align: right;">${new Date(msg.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</div>
`;
chatMessages.appendChild(div);
});
chatMessages.scrollTop = chatMessages.scrollHeight;
lastMessageCount = messages.length;
} catch (error) {
console.error('Chat error:', error);
}
}
async function sendChatMessage(msgContent = null, type = 'text') {
const userNameInput = document.getElementById('user-name');
const userPhoneInput = document.getElementById('user-phone');
const user = userNameInput.value.trim();
const phone = userPhoneInput.value.trim();
const message = msgContent || chatMsg.value.trim();
const phoneRegex = /^\+?[0-9]{7,15}$/;
if (user.length < 3) {
alert('Por favor, ingresa un nombre válido arriba (mínimo 3 caracteres).');
userNameInput.style.borderColor = '#ff4444';
return;
}
if (!phone) {
alert('Por favor, regístrate con tu móvil arriba para poder chatear.');
userPhoneInput.style.borderColor = '#ff4444';
return;
}
if (!phoneRegex.test(phone.replace(/\s/g, ''))) {
alert('Por favor, ingresa un número de móvil válido arriba.');
userPhoneInput.style.borderColor = '#ff4444';
return;
}
if (!message) return;
try {
const response = await fetch('api/chat.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: user, message: message, type: type })
});
const result = await response.json();
if (result.success) {
if (!msgContent) chatMsg.value = '';
fetchMessages();
}
} catch (error) {
console.error('Error sending message:', error);
}
}
chatMsg.addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendChatMessage();
});
async function updatePhotoCounter() {
try {
const response = await fetch('api/photo_status.php');
const data = await response.json();
const counter = document.getElementById('photo-counter');
const cameraBtn = document.getElementById('camera-btn');
const fileInput = document.getElementById('chat-file');
const limitMsg = document.getElementById('photo-limit-msg');
if (data && typeof data.count !== 'undefined') {
counter.innerText = `${data.count}/${data.limit}`;
counter.style.display = 'block';
// Alerta cuando solo queda 1 foto (4 de 5)
if (data.count === data.limit - 1) {
counter.classList.add('counter-alert');
} else {
counter.classList.remove('counter-alert');
}
if (data.count >= data.limit) {
counter.style.background = '#ff4444';
// Bloquear visualmente el botón
cameraBtn.style.opacity = '0.4';
cameraBtn.style.filter = 'grayscale(1)';
cameraBtn.style.pointerEvents = 'none';
cameraBtn.title = 'Límite de fotos alcanzado';
fileInput.disabled = true;
// Mostrar tiempo restante
if (data.reset_in_seconds > 0) {
const hours = Math.floor(data.reset_in_seconds / 3600);
const minutes = Math.floor((data.reset_in_seconds % 3600) / 60);
const timeStr = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
limitMsg.innerText = `Disponible en ${timeStr}`;
limitMsg.style.display = 'block';
}
} else {
counter.style.background = 'var(--primary-color)';
// Restaurar el botón
cameraBtn.style.opacity = '1';
cameraBtn.style.filter = 'none';
cameraBtn.style.pointerEvents = 'auto';
cameraBtn.title = 'Subir foto';
fileInput.disabled = false;
limitMsg.style.display = 'none';
}
}
} catch (error) {
console.error('Error updating photo counter:', error);
}
}
// Initial fetch and poll
fetchMessages();
updatePhotoCounter();
setInterval(fetchMessages, 3000);
setInterval(updatePhotoCounter, 30000); // Check limit status every 30s
// --- End Chat Functionality ---
function openQRModal() {
const modal = document.getElementById('qr-modal');
modal.style.display = 'flex';
setTimeout(() => {
modal.classList.add('show');
}, 10);
}
function closeQRModal() {
const modal = document.getElementById('qr-modal');
modal.classList.remove('show');
setTimeout(() => {
modal.style.display = 'none';
}, 300);
}
// Close modal on Esc key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeQRModal();
});
async function likeSong() {
const nameInput = document.getElementById('user-name');
const name = nameInput.value.trim();
const song = document.getElementById('track-title').innerText.replace(/\s{2,}/g, ' ').trim();
if (name.length < 3) {
alert('Por favor, ingresa tu nombre arriba para dar Like.');
nameInput.style.borderColor = '#ff4444';
return;
}
const btn = document.getElementById('like-song-btn');
const icon = btn.querySelector('i');
icon.classList.remove('bi-heart');
icon.classList.add('bi-heart-fill');
btn.style.transform = 'scale(1.3)';
try {
const response = await fetch('api/like_song.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: name, song_title: song })
});
const result = await response.json();
if (result.success) {
// Confetti explosion!
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 },
colors: ['#ff4444', '#00e676', '#38bdf8', '#facc15']
});
document.getElementById('like-count').innerText = result.likes_count;
setTimeout(() => {
btn.style.transform = 'scale(1)';
}, 200);
fetchMessages();
fetchTopFans(); // Update ranking
}
} catch (error) {
console.error('Error liking song:', error);
}
}
let recentTracks = JSON.parse(localStorage.getItem('recentTracks') || '[]');
function renderRecentTracks() {
const container = document.getElementById('recent-tracks-container');
const list = document.getElementById('recent-tracks-list');
if (!list || recentTracks.length === 0) {
if (container) container.style.display = 'none';
return;
}
if (container) container.style.display = 'block';
list.innerHTML = '';
recentTracks.forEach(track => {
const item = document.createElement('div');
item.style.display = 'flex';
item.style.alignItems = 'center';
item.style.gap = '10px';
item.style.background = 'rgba(255,255,255,0.03)';
item.style.padding = '5px 10px';
item.style.borderRadius = '8px';
item.style.fontSize = '0.75rem';
item.style.border = '1px solid rgba(255,255,255,0.05)';
const likesCount = track.likes || '0';
item.innerHTML = `
<img src="${track.cover}" style="width: 30px; height: 30px; border-radius: 4px; object-fit: cover;">
<div style="flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; opacity: 0.7;">
${track.title}
</div>
<div style="display: flex; align-items: center; gap: 3px; color: #ff4444; font-weight: bold; opacity: 0.9;">
<i class="bi bi-heart-fill" style="font-size: 0.65rem;"></i>
<span>${likesCount}</span>
</div>
`;
list.appendChild(item);
});
}
// Initial render
renderRecentTracks();
async function fetchTopSongs() {
try {
const response = await fetch('api/top-songs.php');
const result = await response.json();
if (result.success && result.data) {
const list = document.getElementById('top-songs-list');
if (result.data.length === 0) {
list.innerHTML = '<div style="font-size: 0.8rem; opacity: 0.5; text-align: center;">Aún no hay votos esta semana.</div>';
return;
}
list.innerHTML = result.data.map((song, index) => `
<div style="display: flex; align-items: center; justify-content: space-between; background: rgba(255,255,255,0.05); padding: 0.6rem 0.8rem; border-radius: 10px; border: 1px solid rgba(255,255,255,0.05); transition: transform 0.2s; cursor: default;" onmouseover="this.style.transform='translateX(5px)'" onmouseout="this.style.transform='translateX(0)'">
<div style="display: flex; align-items: center; gap: 10px; overflow: hidden;">
<span style="font-weight: 800; color: #facc15; font-size: 0.9rem; min-width: 20px;">#${index + 1}</span>
<span style="font-size: 0.85rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${song.song_title}</span>
</div>
<div style="display: flex; align-items: center; gap: 4px; color: #ff4444; font-weight: bold; font-size: 0.8rem;">
<i class="bi bi-heart-fill"></i>
<span>${song.likes_count}</span>
</div>
</div>
`).join('');
}
} catch (error) {
console.error('Error fetching top songs:', error);
}
}
// Fetch Now Playing Metadata from RadioKing
async function updateMetadata() {
try {
const response = await fetch('https://www.radioking.com/widgets/api/v1/radio/828046/track/current');
const data = await response.json();
if (data && data.title) {
const title = data.title;
const artist = data.artist || 'Lili Records';
const coverUrl = data.cover || './assets/pasted-20260215-163754-def41f49.png';
const fullDisplay = title.includes(artist) ? title : `${artist} - ${title}`;
if (trackTitle.textContent !== title || (trackArtist && trackArtist.textContent !== artist)) {
// Add previous song to history before changing
if (trackTitle.textContent !== "Cargando stream..." && trackTitle.textContent !== "Lili Records Radio - En Vivo") {
const prevTrack = {
title: trackTitle.textContent + (trackArtist ? ' - ' + trackArtist.textContent : ''),
cover: trackCover.src,
likes: document.getElementById('like-count').innerText || '0'
};
if (recentTracks.length === 0 || recentTracks[0].title !== prevTrack.title) {
recentTracks.unshift(prevTrack);
if (recentTracks.length > 5) recentTracks.pop();
localStorage.setItem('recentTracks', JSON.stringify(recentTracks));
renderRecentTracks();
}
}
trackTitle.style.opacity = '0';
if (trackArtist) trackArtist.style.opacity = '0';
if (trackCover) trackCover.style.opacity = '0';
// Reset Like Button
const likeIcon = document.querySelector('#like-song-btn i');
if (likeIcon) {
likeIcon.classList.remove('bi-heart-fill');
likeIcon.classList.add('bi-heart');
}
const likeCount = document.getElementById('like-count');
if (likeCount) likeCount.innerText = '';
trackTitle.classList.remove('scrolling');
setTimeout(async () => {
trackTitle.textContent = title;
if (trackArtist) trackArtist.textContent = artist;
trackTitle.style.opacity = '1';
if (trackArtist) trackArtist.style.opacity = '0.8';
if (trackCover) {
trackCover.src = coverUrl;
trackCover.style.display = 'block';
if (coverPlaceholder) coverPlaceholder.style.display = 'none';
trackCover.style.opacity = '1';
}
// Fetch likes for this song
try {
const lResp = await fetch(`api/get_likes.php?song_title=${encodeURIComponent(fullDisplay)}`);
const lData = await lResp.json();
if (lData.likes_count > 0 && likeCount) {
likeCount.innerText = lData.likes_count;
}
} catch (e) {}
// Check if scrolling is needed
if (trackTitle.scrollWidth > trackTitle.clientWidth) {
trackTitle.classList.add('scrolling');
trackTitle.innerHTML = `<span>${title} &nbsp;&nbsp;&nbsp;&nbsp; ${title} &nbsp;&nbsp;&nbsp;&nbsp;</span>`;
}
}, 500);
}
document.title = `▶ ${fullDisplay} | Lili Records Radio`;
// Media Session API for System Controls
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: title,
artist: artist,
album: 'Lili Records Radio',
artwork: [
{ src: data.cover || './assets/pasted-20260215-163754-def41f49.png', sizes: '512x512', type: 'image/png' },
{ src: data.cover || './assets/pasted-20260215-163754-def41f49.png', sizes: '256x256', type: 'image/png' }
]
});
}
} else {
trackTitle.textContent = "Lili Records Radio - En Vivo";
}
} catch (error) {
console.error('Error fetching metadata:', error);
if (trackTitle.textContent === "Cargando stream...") {
trackTitle.textContent = "Lili Records Radio - En Vivo";
}
}
}
// Update every 30 seconds
updateMetadata();
fetchTopSongs();
setInterval(() => {
updateMetadata();
fetchTopSongs();
}, 30000);
// Handle possible audio interruptions
audio.addEventListener('error', function(e) {
console.error('Audio error:', e);
trackTitle.textContent = "Error de conexión. Reintentando...";
setTimeout(() => {
audio.load();
if (!audio.paused) audio.play();
}, 5000);
});
</script>
</body>
</html>