38018-vm/index.php
2026-01-31 13:50:15 +00:00

705 lines
23 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
$requests = [];
try {
$stmt = db()->query("SELECT name, phone, message, created_at FROM listener_requests ORDER BY created_at DESC LIMIT 20");
$requests = $stmt->fetchAll();
} 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;
}
</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">Conectando...</span>
<span class="d-block opacity-50 small" id="artistName">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>
<?php if ($isAdmin): ?>
<div class="mt-4 pt-4 border-top border-white border-opacity-10 position-relative" style="z-index: 2;">
<div class="mb-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">Cerrar Sesión</a>
</div>
<?php endif; ?>
</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 + Comments -->
<div class="right-content">
<img src="<?= $promoImage ?>" alt="Lili Records Promo" class="promo-image">
<div class="comments-window">
<?php if ($isAdmin): ?>
<div class="comments-header">
<div>
<h5 class="mb-1"><i class="fas fa-comments me-2 text-primary"></i>Mensajes de Oyentes</h5>
<p class="small opacity-50 mb-0">Gestiona las peticiones en tiempo real</p>
</div>
<div class="text-center" style="width: 90px;">
<img src="<?= $qrImage ?>" alt="QR Pago" class="w-100 rounded-3 mb-1">
<p class="small fw-bold mb-0">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 (!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>
<?php else: ?>
<div class="comments-header border-0 mb-3">
<div>
<h5 class="mb-1"><i class="fas fa-paper-plane me-2 text-primary"></i>¡Envía tu mensaje!</h5>
<p class="small opacity-60 mb-0">Pide una canción o envía un saludo al aire.</p>
</div>
<div class="text-center" style="width: 90px;">
<img src="<?= $qrImage ?>" alt="QR Pago" class="w-100 rounded-3 mb-1">
<p class="small fw-bold mb-0">APOYAR</p>
</div>
</div>
<div class="comments-list" style="max-height: 250px;">
<?php if (empty($requests)): ?>
<p class="text-center py-4 opacity-40 italic">Sé el primero en escribir...</p>
<?php else: ?>
<?php foreach (array_slice($requests, 0, 8) as $req): ?>
<div class="comment-item py-2 px-3 mb-2 border-0 bg-white bg-opacity-5 rounded-4">
<span class="comment-user small"><?= htmlspecialchars($req['name']) ?></span>
<p class="mb-0 small"><?= htmlspecialchars($req['message']) ?></p>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php endif; ?>
<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 form-control-glass" placeholder="Tu nombre" required>
</div>
<div class="col-sm-6">
<input type="text" name="phone" class="form-control form-control-glass" placeholder="Tu WhatsApp">
</div>
</div>
<div class="input-group">
<textarea name="message" class="form-control form-control-glass" rows="1" placeholder="Mensaje o canción..." required></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>
<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');
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; // Fewer bars for a cleaner, modern look
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;
// We only use the first half of the frequency data because the high end is often empty
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; // Minimum bar height for visibility
// Create a vibrant gradient for each bar
const gradient = ctx.createLinearGradient(0, height - barHeight, 0, height);
gradient.addColorStop(0, '#ff2d55'); // Hot Pink
gradient.addColorStop(0.5, '#ff512f'); // Vibrant Orange-Red
gradient.addColorStop(1, 'rgba(255, 45, 85, 0.2)');
ctx.fillStyle = gradient;
// Draw stylized bars with rounded tops
const bx = x + 2; // small padding
const bw = barWidth - 4; // bar thickness
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() {
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>