diff --git a/api/automation.php b/api/automation.php index 03c388b..a334d99 100644 --- a/api/automation.php +++ b/api/automation.php @@ -5,18 +5,82 @@ require_once __DIR__ . '/../db/config.php'; try { $db = db(); - // 1. Check if there is an active "played" song within its duration (e.g. 5 minutes) - $stmt = $db->query("SELECT COUNT(*) FROM song_requests WHERE status = 'played' AND created_at > DATE_SUB(NOW(), INTERVAL 5 MINUTE)"); - $hasActive = $stmt->fetchColumn(); + // 1. Check if there is an active local "played" song and if it's still playing + $stmt = $db->query("SELECT *, UNIX_TIMESTAMP(created_at) as started_at FROM song_requests WHERE status = 'played' ORDER BY created_at DESC LIMIT 1"); + $lastPlayed = $stmt->fetch(); - if ($hasActive == 0) { - // 2. No active song? Let's take the oldest pending request and "execute" it + $isLocalActive = false; + if ($lastPlayed) { + $duration = (int)($lastPlayed['duration'] ?? 240); + $endTime = $lastPlayed['started_at'] + $duration; + if (time() < $endTime) { + $isLocalActive = true; + } + } + + // 2. If no local song is active, check if RadioKing is playing something we should wait for + $shouldWait = $isLocalActive; + + if (!$isLocalActive) { + // Optional: Check RadioKing current track end_at + try { + $radioKingUrl = 'https://www.radioking.com/widgets/api/v1/radio/828046/track/current'; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $radioKingUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 3); + $resp = curl_exec($ch); + curl_close($ch); + + if ($resp) { + $data = json_decode($resp, true); + if (isset($data['end_at'])) { + $endAt = strtotime($data['end_at']); + // If the RadioKing song ends in more than 5 seconds, we wait + if ($endAt > (time() + 5)) { + $shouldWait = true; + } + } + } + } catch (Exception $e) { + // Ignore RadioKing errors and proceed + } + } + + if (!$shouldWait) { + // 3. Take the oldest pending request and "execute" it $stmt = $db->query("SELECT * FROM song_requests WHERE status = 'pending' ORDER BY is_priority DESC, created_at ASC LIMIT 1"); $next = $stmt->fetch(); if ($next) { - $stmt = $db->prepare("UPDATE song_requests SET status = 'played' WHERE id = ?"); - $stmt->execute([$next['id']]); + // Find YouTube info before playing if missing + $youtubeUrl = $next['youtube_url']; + $duration = $next['duration'] ?? 240; + + if (!$youtubeUrl) { + try { + require_once __DIR__ . '/../ai/LocalAIApi.php'; + $artist = $next['artist']; + $song = $next['song']; + $aiResp = LocalAIApi::createResponse([ + 'input' => [ + ['role' => 'system', 'content' => 'Eres un asistente de radio. Tu tarea es encontrar el ID de YouTube oficial y la duración aproximada en segundos para una canción. Responde ÚNICAMENTE con un JSON: {"id": "...", "duration": ...}. Si no estás seguro, usa id null y duration 240.'], + ['role' => 'user', 'content' => "Encuentra el ID de YouTube y duración para: $artist - $song"] + ] + ]); + if (!empty($aiResp['success'])) { + $jsonText = LocalAIApi::extractText($aiResp); + $data = json_decode($jsonText, true); + if ($data && isset($data['id']) && $data['id'] !== null) { + $youtubeUrl = "https://www.youtube.com/watch?v={$data['id']}"; + $duration = (int)($data['duration'] ?? 240); + } + } + } catch (Exception $e) {} + } + + $stmt = $db->prepare("UPDATE song_requests SET status = 'played', youtube_url = ?, duration = ?, created_at = NOW() WHERE id = ?"); + $stmt->execute([$youtubeUrl, $duration, $next['id']]); // Log automation $db->prepare("INSERT INTO automation_logs (message) VALUES (?)")->execute(["Auto-DJ ejecutó: {$next['artist']} - {$next['song']}"]); diff --git a/api/current_song.php b/api/current_song.php index 533c30d..adff740 100644 --- a/api/current_song.php +++ b/api/current_song.php @@ -5,22 +5,32 @@ require_once __DIR__ . '/../db/config.php'; try { $db = db(); - // Check for a "manual override" song that was recently marked as played - // We look for a song marked as 'played' in the last 4 minutes (typical song duration) - $stmt = $db->query("SELECT artist, song, requester, youtube_url FROM song_requests WHERE status = 'played' AND created_at > DATE_SUB(NOW(), INTERVAL 4 MINUTE) ORDER BY created_at DESC LIMIT 1"); + // Check for a local "played" song that is currently within its duration + $stmt = $db->query("SELECT artist, song, requester, youtube_url, duration, UNIX_TIMESTAMP(created_at) as started_at FROM song_requests WHERE status = 'played' ORDER BY created_at DESC LIMIT 1"); $override = $stmt->fetch(); if ($override) { - echo json_encode([ - 'success' => true, - 'source' => 'local_request', - 'artist' => $override['artist'], - 'title' => $override['song'], - 'requester' => $override['requester'], - 'youtube_url' => $override['youtube_url'], - 'cover' => './assets/pasted-20260215-163754-def41f49.png' - ]); - exit; + $duration = (int)($override['duration'] ?? 240); + $startedAt = $override['started_at']; + $endAt = $startedAt + $duration; + $remaining = $endAt - time(); + + if ($remaining > 0) { + echo json_encode([ + 'success' => true, + 'source' => 'local_request', + 'artist' => $override['artist'], + 'title' => $override['song'], + 'requester' => $override['requester'], + 'youtube_url' => $override['youtube_url'], + 'duration' => $duration, + 'started_at' => date('c', $startedAt), + 'end_at' => date('c', $endAt), + 'remaining' => $remaining, + 'cover' => './assets/pasted-20260215-163754-def41f49.png' + ]); + exit; + } } // Fallback: Proxy RadioKing metadata @@ -35,19 +45,27 @@ try { if ($httpCode === 200 && $resp) { $data = json_decode($resp, true); + $duration = $data['duration'] ?? 0; + $endAt = $data['end_at'] ?? null; + $startedAt = null; + + if ($endAt && $duration) { + $startedAt = date('c', strtotime($endAt) - $duration); + } + echo json_encode([ 'success' => true, 'source' => 'radioking', 'artist' => $data['artist'] ?? 'Lili Records', 'title' => $data['title'] ?? 'La mejor música', 'cover' => $data['cover'] ?? './assets/pasted-20260215-163754-def41f49.png', - 'duration' => $data['duration'] ?? 0, - 'end_at' => $data['end_at'] ?? null, + 'duration' => $duration, + 'started_at' => $startedAt, + 'end_at' => $endAt, 'next_track' => $data['next_track'] ?? null ]); - } else { - echo json_encode(['success' => false, 'error' => 'Failed to fetch RadioKing metadata']); } + } catch (Exception $e) { echo json_encode(['success' => false, 'error' => $e->getMessage()]); } diff --git a/api/song_requests.php b/api/song_requests.php index 82b3126..85584d0 100644 --- a/api/song_requests.php +++ b/api/song_requests.php @@ -83,39 +83,42 @@ if ($method === 'POST') { $wasAutoExecuted = true; } - $stmt = $db->prepare("INSERT INTO song_requests (artist, song, requester, source, status) VALUES (?, ?, ?, ?, ?)"); - $stmt->execute([$artist, $song, $requester, $source, $initialStatus]); + $stmt = $db->prepare("INSERT INTO song_requests (artist, song, requester, source, status, duration) VALUES (?, ?, ?, ?, ?, ?)"); + $stmt->execute([$artist, $song, $requester, $source, $initialStatus, 240]); + $requestId = $db->lastInsertId(); if ($wasAutoExecuted) { - // Try to find a YouTube link for the song using AI + // Try to find a YouTube link and duration for the song using AI $youtubeUrl = null; + $duration = 240; try { require_once __DIR__ . '/../ai/LocalAIApi.php'; $aiResp = LocalAIApi::createResponse([ 'input' => [ - ['role' => 'system', 'content' => 'Eres un asistente de radio. Tu tarea es encontrar el ID de YouTube oficial para una canción. Responde ÚNICAMENTE con el ID del video (ej: dQw4w9WgXcQ). Si no estás seguro, responde "None".'], - ['role' => 'user', 'content' => "Encuentra el ID de YouTube para: $artist - $song"] + ['role' => 'system', 'content' => 'Eres un asistente de radio. Tu tarea es encontrar el ID de YouTube oficial y la duración aproximada en segundos para una canción. Responde ÚNICAMENTE con un JSON: {"id": "...", "duration": ...}. Si no estás seguro, usa id null y duration 240.'], + ['role' => 'user', 'content' => "Encuentra el ID de YouTube y duración para: $artist - $song"] ] ]); if (!empty($aiResp['success'])) { - $id = trim(LocalAIApi::extractText($aiResp)); - if ($id !== 'None' && strlen($id) > 5 && strlen($id) < 15) { - $youtubeUrl = "https://www.youtube.com/watch?v=$id"; + $jsonText = LocalAIApi::extractText($aiResp); + $data = json_decode($jsonText, true); + if ($data && isset($data['id']) && $data['id'] !== null) { + $youtubeUrl = "https://www.youtube.com/watch?v={$data['id']}"; + $duration = (int)($data['duration'] ?? 240); } } } catch (Exception $e) { error_log("AI YouTube search failed: " . $e->getMessage()); } - $stmt = $db->prepare("INSERT INTO song_requests (artist, song, requester, source, status, youtube_url) VALUES (?, ?, ?, ?, ?, ?)"); - $stmt->execute([$artist, $song, $requester, $source, $initialStatus, $youtubeUrl]); + if ($youtubeUrl || $duration != 240) { + $stmt = $db->prepare("UPDATE song_requests SET youtube_url = ?, duration = ? WHERE id = ?"); + $stmt->execute([$youtubeUrl, $duration, $requestId]); + } // Announce auto-execution $chatMsg = "🚀 [AUTO-DJ] ¡Petición de **$requester** ejecutada automáticamente! 🎶 Sonando: **$artist - $song**" . ($youtubeUrl ? " (con video)" : ""); $db->prepare("INSERT INTO messages (username, message, type) VALUES ('Sistema', ?, 'dj_power')")->execute([$chatMsg]); - } else { - $stmt = $db->prepare("INSERT INTO song_requests (artist, song, requester, source, status) VALUES (?, ?, ?, ?, ?)"); - $stmt->execute([$artist, $song, $requester, $source, $initialStatus]); } diff --git a/index.php b/index.php index c467b46..81b62fb 100644 --- a/index.php +++ b/index.php @@ -4624,7 +4624,17 @@ $twitter_link = "https://twitter.com/"; if (percent > 100) percent = 100; if (percent < 0) percent = 0; progBar.style.width = percent + '%'; - if (percent >= 100) clearInterval(progressInterval); + if (percent >= 100) { + clearInterval(progressInterval); + // Play transition sound + transitionAudio.volume = 0.5; + transitionAudio.play().catch(e => console.log('Transition sound blocked:', e)); + + // Trigger automation check and metadata refresh immediately + fetch('api/automation.php').finally(() => { + setTimeout(updateMetadata, 1000); + }); + } } updateProgressBar(); @@ -4691,6 +4701,7 @@ $twitter_link = "https://twitter.com/"; const ticTacAudio = new Audio('https://assets.mixkit.co/active_storage/sfx/560/560-preview.mp3'); const applauseAudio = new Audio('https://assets.mixkit.co/active_storage/sfx/467/467-preview.mp3'); // Crowd Cheer + const transitionAudio = new Audio('https://assets.mixkit.co/active_storage/sfx/2013/2013-preview.mp3'); // Ding function testTicTac() { const btn = document.getElementById('test-tictac-btn');