38018-vm/index.php
2026-01-31 01:47:16 +00:00

755 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.2);
--glass-border: rgba(255, 255, 255, 0.2);
}
body {
font-family: 'Inter', sans-serif;
background: #0a0a0a;
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.4) blur(3px);
z-index: -1;
}
.main-wrapper {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: center;
width: 100%;
max-width: 1300px;
padding: 2rem;
gap: 3rem;
z-index: 1;
}
@media (max-width: 992px) {
.main-wrapper {
flex-direction: column;
align-items: center;
padding: 1rem;
gap: 2rem;
}
body {
overflow-y: auto;
height: auto;
}
}
.player-container {
background: var(--glass-bg);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-radius: 20px;
padding: 1.25rem;
flex: 0 0 300px;
max-width: 300px;
width: 100%;
text-align: center;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
position: sticky;
top: 2rem;
transition: all 0.3s ease;
}
.right-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 2rem;
max-width: 600px;
width: 100%;
}
.promo-image {
width: 100%;
height: auto;
border-radius: 24px;
box-shadow: 0 20px 50px rgba(0,0,0,0.6);
border: 1px solid var(--glass-border);
transition: transform 0.5s ease;
}
.comments-window {
background: var(--glass-bg);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-radius: 24px;
padding: 1.5rem;
box-shadow: 0 20px 50px rgba(0,0,0,0.4);
display: flex;
flex-direction: column;
max-height: 700px;
}
.comments-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--glass-border);
}
.comments-list {
overflow-y: auto;
flex: 1;
padding-right: 0.5rem;
}
.comments-list::-webkit-scrollbar {
width: 4px;
}
.comments-list::-webkit-scrollbar-thumb {
background: var(--glass-border);
border-radius: 10px;
}
.comment-item {
background: rgba(255,255,255,0.05);
border-radius: 12px;
padding: 1rem;
margin-bottom: 1rem;
font-size: 0.9rem;
position: relative;
transition: all 0.3s ease;
}
.comment-item:hover {
background: rgba(255,255,255,0.08);
}
.comment-user {
font-weight: 700;
color: var(--primary-color);
margin-bottom: 0.2rem;
display: block;
}
.comment-text {
opacity: 0.9;
}
.comment-text {
opacity: 0.9;
}
.comment-date {
font-size: 0.7rem;
opacity: 0.4;
display: block;
margin-top: 0.4rem;
}
.comment-actions {
position: absolute;
top: 10px;
right: 10px;
}
.btn-wa-reply {
background: #25d366;
color: #fff;
border: none;
border-radius: 50%;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
transition: all 0.3s ease;
text-decoration: none;
}
.btn-wa-reply:hover {
transform: scale(1.1);
color: #fff;
}
.request-form {
margin-top: 1rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.form-control-glass::placeholder {
color: #666666;
opacity: 1;
}
.form-control-glass {
caret-color: var(--primary-color);
background: #ffffff;
border: 1px solid var(--glass-border);
color: #000000;
border-radius: 12px;
padding: 0.6rem 1rem;
}
.form-control-glass:focus {
background: #ffffff;
border-color: #cccccc;
box-shadow: none;
color: #000000;
}
.btn-send-request {
background: var(--primary-color);
border: none;
border-radius: 12px;
padding: 0.7rem;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-send-request:hover {
background: #ff512f;
transform: translateY(-2px);
}
.radio-logo {
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #ff2d55, #ff512f);
margin: 0 auto 0.75rem;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 20px rgba(255, 45, 85, 0.4);
animation: pulse 2s infinite;
overflow: hidden;
border: 2px solid rgba(255,255,255,0.2);
}
.radio-logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
@keyframes pulse {
0% { transform: scale(1); box-shadow: 0 0 20px rgba(255, 45, 85, 0.4); }
50% { transform: scale(1.05); box-shadow: 0 0 35px rgba(255, 45, 85, 0.6); }
100% { transform: scale(1); box-shadow: 0 0 20px rgba(255, 45, 85, 0.4); }
}
.song-info {
margin-bottom: 1rem;
}
.song-title {
font-size: 1rem;
font-weight: 700;
margin-bottom: 0.1rem;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.artist-name {
font-size: 0.8rem;
opacity: 0.7;
}
.player-controls-row {
display: flex;
align-items: center;
gap: 0.75rem;
margin-top: 0.5rem;
}
.btn-play {
width: 44px;
height: 44px;
border-radius: 50%;
background: #fff;
color: #000;
border: none;
font-size: 1.1rem;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
cursor: pointer;
flex-shrink: 0;
}
.btn-play:hover {
transform: scale(1.05);
background: var(--primary-color);
color: #fff;
}
.volume-slider-container {
flex: 1;
display: flex;
align-items: center;
gap: 0.5rem;
}
.volume-slider {
width: 100%;
height: 4px;
-webkit-appearance: none;
background: rgba(255,255,255,0.2);
border-radius: 2px;
outline: none;
}
.btn-youtube-float {
position: fixed;
bottom: 105px;
left: 30px;
width: 60px;
height: 60px;
background: #ff0000;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
box-shadow: 0 10px 30px rgba(255, 0, 0, 0.4);
text-decoration: none;
transition: all 0.3s ease;
z-index: 100;
animation: bounce 2.2s infinite;
}
.btn-youtube-float:hover {
transform: translateY(-5px) scale(1.1);
color: #fff;
background: #e60000;
animation: none;
}
.qr-section {
margin-top: 0.75rem;
padding-top: 0.75rem;
border-top: 1px solid var(--glass-border);
text-align: center;
}
.qr-image {
width: 80px;
height: 80px;
border-radius: 10px;
margin: 0.25rem auto;
border: 2px solid rgba(255,255,255,0.1);
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}
.whatsapp-btn {
position: fixed;
bottom: 30px;
left: 30px;
width: 60px;
height: 60px;
background: #25d366;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
box-shadow: 0 10px 30px rgba(37, 211, 102, 0.4);
text-decoration: none;
transition: all 0.3s ease;
z-index: 100;
animation: bounce 2s infinite;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {transform: translateY(0);}
40% {transform: translateY(-10px);}
60% {transform: translateY(-5px);}
}
.whatsapp-btn:hover {
transform: translateY(-5px) scale(1.1);
color: #fff;
animation: none;
}
.live-badge {
position: absolute;
top: 10px;
left: 10px;
background: var(--primary-color);
padding: 2px 8px;
border-radius: 20px;
font-size: 0.6rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
}
#visualizer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 40px;
opacity: 0.15;
z-index: -1;
}
/* Policy Modal Styles */
.modal-glass .modal-content {
background: rgba(15, 15, 15, 0.9);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
color: #fff;
border-radius: 24px;
}
.modal-glass .modal-header {
border-bottom: 1px solid var(--glass-border);
}
.modal-glass .modal-footer {
border-top: 1px solid var(--glass-border);
}
.policy-link {
color: #fff;
text-decoration: underline;
opacity: 0.6;
font-size: 0.75rem;
transition: opacity 0.3s ease;
cursor: pointer;
display: inline-block;
margin-top: 0.5rem;
}
.policy-link:hover {
opacity: 1;
color: var(--primary-color);
}
.policy-content-text {
font-size: 0.9rem;
line-height: 1.6;
opacity: 0.9;
}
.policy-content-text ul {
padding-left: 1.2rem;
}
.policy-content-text li {
margin-bottom: 0.75rem;
}
</style>
</head>
<body>
<div class="bg-image" id="bgImage"></div>
<div class="main-wrapper">
<!-- Left: Player -->
<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">
<span class="song-title" id="songTitle">Conectando...</span>
<span class="artist-name" id="artistName">Lili Records Radio</span>
</div>
<div class="player-controls-row">
<button class="btn-play" id="playBtn">
<i class="fas fa-play" id="playIcon"></i>
</button>
<div class="volume-slider-container">
<i class="fas fa-volume-low opacity-50 x-small" style="font-size: 0.7rem;"></i>
<input type="range" class="volume-slider" id="volumeSlider" min="0" max="1" step="0.01" value="0.8">
<i class="fas fa-volume-high opacity-50 x-small" style="font-size: 0.7rem;"></i>
</div>
</div>
<canvas id="visualizer"></canvas>
<?php if ($isAdmin): ?>
<div class="mt-3">
<a href="?logout=1" class="btn btn-sm btn-outline-light opacity-50" style="font-size: 0.7rem;">Cerrar Admin</a>
</div>
<?php endif; ?>
</aside>
<!-- 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">
<h5 class="mb-0"><i class="fas fa-comments me-2"></i>Mensajes de la Audiencia</h5>
<span class="badge bg-danger rounded-pill"><?= count($requests) ?></span>
</div>
<div class="comments-list" id="commentsList">
<?php if (empty($requests)): ?>
<div class="text-center py-5 opacity-50">
<i class="fas fa-comment-dots fa-3x mb-3"></i>
<p>No hay mensajes todavía.<br>¡Conéctate con Lili Records!</p>
</div>
<?php else: ?>
<?php foreach ($requests as $req): ?>
<div class="comment-item">
<span class="comment-user"><?= htmlspecialchars($req['name']) ?></span>
<p class="comment-text mb-0"><?= htmlspecialchars($req['message']) ?></p>
<span class="comment-date"><?= date('d M, H:i', strtotime($req['created_at'])) ?></span>
<?php if (!empty($req['phone'])): ?>
<div class="comment-actions">
<a href="https://wa.me/<?= preg_replace('/[^0-9]/', '', $req['phone']) ?>" class="btn-wa-reply" title="Chatear por WhatsApp: <?= htmlspecialchars($req['phone']) ?>" target="_blank">
<i class="fab fa-whatsapp"></i>
</a>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php else: ?>
<div class="comments-header border-0 mb-0">
<h5 class="mb-0"><i class="fas fa-paper-plane me-2"></i>¡Envíanos tu mensaje!</h5>
</div>
<p class="small opacity-50 mb-3">Tus peticiones y saludos llegan directo a cabina.</p>
<?php endif; ?>
<div class="qr-section">
<img src="<?= $qrImage ?>" alt="QR Pago" class="qr-image">
<p class="small opacity-50 mt-1" style="font-size: 0.7rem;">Apoya a la radio</p>
</div>
<form action="submit_request.php" method="POST" class="request-form">
<div class="row g-2">
<div class="col-6">
<input type="text" name="name" class="form-control form-control-glass" placeholder="Tu nombre" required>
</div>
<div class="col-6">
<input type="text" name="phone" class="form-control form-control-glass" placeholder="WhatsApp (opcional)">
</div>
</div>
<div class="input-group">
<textarea name="message" class="form-control form-control-glass" rows="1" placeholder="Tu mensaje aquí..." required></textarea>
<button type="submit" class="btn btn-send-request text-white">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<small class="text-center opacity-50" style="font-size: 0.7rem;">Tu mensaje se enviará de forma privada a los administradores.</small>
</form>
</div>
<div class="mt-3 text-center">
<p class="small text-white opacity-75 mb-0">Lili Record´s Radio 2026, Todos los Derechos Reservados</p>
<span class="policy-link" data-bs-toggle="modal" data-bs-target="#policyModal">Política de Privacidad y contenido</span>
</div>
</div>
</div>
<!-- Modal Política de Privacidad -->
<div class="modal fade modal-glass" id="policyModal" tabindex="-1" aria-labelledby="policyModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="policyModalLabel">RADIO LILI RECORD´S POLÍTICA DE PRIVACIDAD Y CONTENIDO</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body policy-content-text">
<ul>
<li>No comentarios indecentes, ofensivos o de discriminación de raza, sexo, credo o ideología.</li>
<li>No aceptamos comentarios, ni contenidos relacionados con política, nuestro objetivo es totalmente de carácter cultural con el fin de interactuar e intercambiar contenidos para crecer como humanos creativos.</li>
<li>La app no recoge ningún dato de carácter personal del usuario como, por ejemplo, nombre, fotografías o localización. Solo en caso de que lo solicite el cliente por intereses personales y con autorización de los administradores y dueño de la aplicación.</li>
<li>En consecuencia la app no comparte ningún dato personal con ninguna otra entidad o terceras personas.</li>
<li>Los contenidos introducidos dígase (canciones, poesías, letras u otra composición) relacionada con la música solo se promoverán, se divulgaran, se escucharan a través de la app a petición del usuario, son enviados al servidor de la app con estos fines y la app que no podrá compartirla si el permiso de su Dueño.</li>
<li>No Permitimos que terceras compañías publiquen anuncios y recopilen cierta información anónima cuando visite nuestra aplicación si no es del interés de ambas partes y sin dañar a nuestros clientes y radios oyentes. Ninguna empresa pueden utilizar información anónima, como su ID de publicidad de Google, el tipo y la versión de su dispositivo, la actividad de navegación, la ubicación y otros datos técnicos relacionados con su dispositivo, a fin de proporcionar anuncios.</li>
<li>Aclaramos nuestro principal objetivo es dar a conocer nuevos contenidos relacionados con la música ya sea creada en IA o interpretación personal. En caso de intereses hacia productos o contenido serviremos como viabilizadores siempre y cuando ambas partes estén de acuerdo.</li>
</ul>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary rounded-pill" data-bs-dismiss="modal">Cerrar</button>
</div>
</div>
</div>
</div>
<a href="<?= $youtubeUrl ?>" target="_blank" class="btn-youtube-float" title="Síguenos en YouTube">
<i class="fab fa-youtube"></i>
</a>
<a href="track_click.php?type=whatsapp_open&redirect=<?= urlencode($whatsappLink) ?>" class="whatsapp-btn" title="Contáctanos">
<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');
let isPlaying = false;
playBtn.addEventListener('click', () => {
if (isPlaying) {
audio.pause();
playIcon.classList.replace('fa-pause', 'fa-play');
} else {
audio.play().catch(e => console.error("Error playing audio:", e));
playIcon.classList.replace('fa-play', 'fa-pause');
}
isPlaying = !isPlaying;
});
volumeSlider.addEventListener('input', (e) => {
audio.volume = e.target.value;
});
// Fetch song metadata from RadioKing
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();
// Simple visualizer effect
const canvas = document.getElementById('visualizer');
const ctx = canvas.getContext('2d');
function resizeCanvas() {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
function drawVisualizer() {
if (!isPlaying) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
requestAnimationFrame(drawVisualizer);
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ff2d55';
const bars = 30;
const barWidth = canvas.width / bars;
for (let i = 0; i < bars; i++) {
const barHeight = Math.random() * canvas.height;
ctx.fillRect(i * barWidth, canvas.height - barHeight, barWidth - 2, barHeight);
}
setTimeout(() => requestAnimationFrame(drawVisualizer), 100);
}
drawVisualizer();
// Scroll to bottom of comments
const commentsList = document.getElementById('commentsList');
if (commentsList) {
commentsList.scrollTop = 0; // Show latest first
}
</script>
</body>
</html>