From d28bf682c95f91f998fcf84d043863f3c3174e5a Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 25 Feb 2026 21:49:22 +0000 Subject: [PATCH] Auto commit: 2026-02-25T21:49:22.586Z --- api/automation.php | 98 +++++++++++++++++++++++++------------------ api/song_requests.php | 68 ++++++++++-------------------- 2 files changed, 79 insertions(+), 87 deletions(-) diff --git a/api/automation.php b/api/automation.php index a334d99..75b43a6 100644 --- a/api/automation.php +++ b/api/automation.php @@ -48,49 +48,65 @@ try { } 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) { - // Find YouTube info before playing if missing - $youtubeUrl = $next['youtube_url']; - $duration = $next['duration'] ?? 240; + // 3. Take the oldest pending request and "execute" it using a transaction to avoid race conditions + $db->beginTransaction(); + try { + $stmt = $db->query("SELECT * FROM song_requests WHERE status = 'pending' ORDER BY is_priority DESC, created_at ASC LIMIT 1 FOR UPDATE"); + $next = $stmt->fetch(); - 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) {} - } + if ($next) { + // Mark as played immediately within the transaction + $stmt = $db->prepare("UPDATE song_requests SET status = 'played', created_at = NOW() WHERE id = ?"); + $stmt->execute([$next['id']]); + $db->commit(); - $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']}"]); - - // Announce in chat - $chatMsg = "🤖 [AUTO-DJ] Es el turno de: **{$next['artist']} - {$next['song']}** (Pedido por: **{$next['requester']}**) 🎧🔥"; - $db->prepare("INSERT INTO messages (username, message, type) VALUES ('Sistema', ?, 'dj_power')")->execute([$chatMsg]); - - echo json_encode(['success' => true, 'action' => 'executed', 'song' => $next['song']]); - exit; + // Now that we "own" this request, we can do the slow AI/log operations + $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) {} + + // Update with AI info + if ($youtubeUrl) { + $stmt = $db->prepare("UPDATE song_requests SET youtube_url = ?, duration = ? 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']}"]); + + // Announce in chat + $chatMsg = "🤖 [AUTO-DJ] Es el turno de: **{$next['artist']} - {$next['song']}** (Pedido por: **{$next['requester']}**) 🎧🔥"; + $db->prepare("INSERT INTO messages (username, message, type) VALUES ('Sistema', ?, 'dj_power')")->execute([$chatMsg]); + + echo json_encode(['success' => true, 'action' => 'executed', 'song' => $next['song']]); + exit; + } else { + $db->rollBack(); + } + } catch (Exception $e) { + $db->rollBack(); + throw $e; } } diff --git a/api/song_requests.php b/api/song_requests.php index fcc6f16..9e226b2 100644 --- a/api/song_requests.php +++ b/api/song_requests.php @@ -70,58 +70,34 @@ if ($method === 'POST') { } try { - // Auto-DJ: Check if we should "execute" this song immediately - // If there's no song marked as 'played' in the last 5 minutes, we execute this one automatically - $checkStmt = $db->query("SELECT COUNT(*) FROM song_requests WHERE status = 'played' AND created_at > DATE_SUB(NOW(), INTERVAL 5 MINUTE)"); - $activePlayed = $checkStmt->fetchColumn(); - - $initialStatus = 'pending'; - $wasAutoExecuted = false; - - if ($activePlayed == 0) { - $initialStatus = 'played'; - $wasAutoExecuted = true; + // Check for duplicates + // 1. Check if it's already in the pending queue + $stmt = $db->prepare("SELECT id FROM song_requests WHERE artist = ? AND song = ? AND status = 'pending' LIMIT 1"); + $stmt->execute([$artist, $song]); + if ($stmt->fetch()) { + echo json_encode(['success' => false, 'error' => 'Esta canción ya está en la cola de espera.']); + exit; } + // 2. Check if it was played in the last 30 minutes (to avoid repetition) + // We skip this check for priority requests or if the DJ is adding it + if ($source !== 'admin') { + $stmt = $db->prepare("SELECT id FROM song_requests WHERE artist = ? AND song = ? AND status = 'played' AND created_at > DATE_SUB(NOW(), INTERVAL 30 MINUTE) LIMIT 1"); + $stmt->execute([$artist, $song]); + if ($stmt->fetch()) { + echo json_encode(['success' => false, 'error' => 'Esta canción se ha reproducido recientemente. ¡Prueba con otra!']); + exit; + } + } + + // All requests start as pending. The automation.php script (called by clients) + // will handle moving them to 'played' status atomically. + $initialStatus = 'pending'; + $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 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 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) { - error_log("AI YouTube search failed: " . $e->getMessage()); - } - - 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]); - } - - // Award points for song request if ($requester !== 'Anónimo') { require_once __DIR__ . '/../includes/points_helper.php';