Auto commit: 2026-02-25T20:04:50.502Z

This commit is contained in:
Flatlogic Bot 2026-02-25 20:04:51 +00:00
parent 2e5c1af7eb
commit ca1b853970
4 changed files with 134 additions and 38 deletions

View File

@ -5,18 +5,82 @@ require_once __DIR__ . '/../db/config.php';
try { try {
$db = db(); $db = db();
// 1. Check if there is an active "played" song within its duration (e.g. 5 minutes) // 1. Check if there is an active local "played" song and if it's still playing
$stmt = $db->query("SELECT COUNT(*) FROM song_requests WHERE status = 'played' AND created_at > DATE_SUB(NOW(), INTERVAL 5 MINUTE)"); $stmt = $db->query("SELECT *, UNIX_TIMESTAMP(created_at) as started_at FROM song_requests WHERE status = 'played' ORDER BY created_at DESC LIMIT 1");
$hasActive = $stmt->fetchColumn(); $lastPlayed = $stmt->fetch();
if ($hasActive == 0) { $isLocalActive = false;
// 2. No active song? Let's take the oldest pending request and "execute" it 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"); $stmt = $db->query("SELECT * FROM song_requests WHERE status = 'pending' ORDER BY is_priority DESC, created_at ASC LIMIT 1");
$next = $stmt->fetch(); $next = $stmt->fetch();
if ($next) { if ($next) {
$stmt = $db->prepare("UPDATE song_requests SET status = 'played' WHERE id = ?"); // Find YouTube info before playing if missing
$stmt->execute([$next['id']]); $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 // Log automation
$db->prepare("INSERT INTO automation_logs (message) VALUES (?)")->execute(["Auto-DJ ejecutó: {$next['artist']} - {$next['song']}"]); $db->prepare("INSERT INTO automation_logs (message) VALUES (?)")->execute(["Auto-DJ ejecutó: {$next['artist']} - {$next['song']}"]);

View File

@ -5,12 +5,17 @@ require_once __DIR__ . '/../db/config.php';
try { try {
$db = db(); $db = db();
// Check for a "manual override" song that was recently marked as played // Check for a local "played" song that is currently within its duration
// 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, duration, UNIX_TIMESTAMP(created_at) as started_at FROM song_requests WHERE status = 'played' ORDER BY created_at DESC LIMIT 1");
$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");
$override = $stmt->fetch(); $override = $stmt->fetch();
if ($override) { if ($override) {
$duration = (int)($override['duration'] ?? 240);
$startedAt = $override['started_at'];
$endAt = $startedAt + $duration;
$remaining = $endAt - time();
if ($remaining > 0) {
echo json_encode([ echo json_encode([
'success' => true, 'success' => true,
'source' => 'local_request', 'source' => 'local_request',
@ -18,10 +23,15 @@ try {
'title' => $override['song'], 'title' => $override['song'],
'requester' => $override['requester'], 'requester' => $override['requester'],
'youtube_url' => $override['youtube_url'], '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' 'cover' => './assets/pasted-20260215-163754-def41f49.png'
]); ]);
exit; exit;
} }
}
// Fallback: Proxy RadioKing metadata // Fallback: Proxy RadioKing metadata
$radioKingUrl = 'https://www.radioking.com/widgets/api/v1/radio/828046/track/current'; $radioKingUrl = 'https://www.radioking.com/widgets/api/v1/radio/828046/track/current';
@ -35,19 +45,27 @@ try {
if ($httpCode === 200 && $resp) { if ($httpCode === 200 && $resp) {
$data = json_decode($resp, true); $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([ echo json_encode([
'success' => true, 'success' => true,
'source' => 'radioking', 'source' => 'radioking',
'artist' => $data['artist'] ?? 'Lili Records', 'artist' => $data['artist'] ?? 'Lili Records',
'title' => $data['title'] ?? 'La mejor música', 'title' => $data['title'] ?? 'La mejor música',
'cover' => $data['cover'] ?? './assets/pasted-20260215-163754-def41f49.png', 'cover' => $data['cover'] ?? './assets/pasted-20260215-163754-def41f49.png',
'duration' => $data['duration'] ?? 0, 'duration' => $duration,
'end_at' => $data['end_at'] ?? null, 'started_at' => $startedAt,
'end_at' => $endAt,
'next_track' => $data['next_track'] ?? null 'next_track' => $data['next_track'] ?? null
]); ]);
} else {
echo json_encode(['success' => false, 'error' => 'Failed to fetch RadioKing metadata']);
} }
} catch (Exception $e) { } catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]); echo json_encode(['success' => false, 'error' => $e->getMessage()]);
} }

View File

@ -83,39 +83,42 @@ if ($method === 'POST') {
$wasAutoExecuted = true; $wasAutoExecuted = true;
} }
$stmt = $db->prepare("INSERT INTO song_requests (artist, song, requester, source, status) VALUES (?, ?, ?, ?, ?)"); $stmt = $db->prepare("INSERT INTO song_requests (artist, song, requester, source, status, duration) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$artist, $song, $requester, $source, $initialStatus]); $stmt->execute([$artist, $song, $requester, $source, $initialStatus, 240]);
$requestId = $db->lastInsertId();
if ($wasAutoExecuted) { 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; $youtubeUrl = null;
$duration = 240;
try { try {
require_once __DIR__ . '/../ai/LocalAIApi.php'; require_once __DIR__ . '/../ai/LocalAIApi.php';
$aiResp = LocalAIApi::createResponse([ $aiResp = LocalAIApi::createResponse([
'input' => [ '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' => '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 para: $artist - $song"] ['role' => 'user', 'content' => "Encuentra el ID de YouTube y duración para: $artist - $song"]
] ]
]); ]);
if (!empty($aiResp['success'])) { if (!empty($aiResp['success'])) {
$id = trim(LocalAIApi::extractText($aiResp)); $jsonText = LocalAIApi::extractText($aiResp);
if ($id !== 'None' && strlen($id) > 5 && strlen($id) < 15) { $data = json_decode($jsonText, true);
$youtubeUrl = "https://www.youtube.com/watch?v=$id"; 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) { } catch (Exception $e) {
error_log("AI YouTube search failed: " . $e->getMessage()); error_log("AI YouTube search failed: " . $e->getMessage());
} }
$stmt = $db->prepare("INSERT INTO song_requests (artist, song, requester, source, status, youtube_url) VALUES (?, ?, ?, ?, ?, ?)"); if ($youtubeUrl || $duration != 240) {
$stmt->execute([$artist, $song, $requester, $source, $initialStatus, $youtubeUrl]); $stmt = $db->prepare("UPDATE song_requests SET youtube_url = ?, duration = ? WHERE id = ?");
$stmt->execute([$youtubeUrl, $duration, $requestId]);
}
// Announce auto-execution // Announce auto-execution
$chatMsg = "🚀 [AUTO-DJ] ¡Petición de **$requester** ejecutada automáticamente! 🎶 Sonando: **$artist - $song**" . ($youtubeUrl ? " (con video)" : ""); $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]); $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]);
} }

View File

@ -4624,7 +4624,17 @@ $twitter_link = "https://twitter.com/";
if (percent > 100) percent = 100; if (percent > 100) percent = 100;
if (percent < 0) percent = 0; if (percent < 0) percent = 0;
progBar.style.width = percent + '%'; 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(); 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 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 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() { function testTicTac() {
const btn = document.getElementById('test-tictac-btn'); const btn = document.getElementById('test-tictac-btn');