![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/public_html/ |
<?php
/**
* Backfill Track Lyrics Script
* Extracts and saves lyrics from task_results JSON files for tracks missing lyrics
*/
require_once 'config/database.php';
$pdo = getDBConnection();
if (!$pdo) {
die("â Database connection failed\n");
}
// Function to extract lyrics from JSON data (same logic as callback.php)
function extractLyricsFromData($data) {
$lyrics = '';
// Check multiple possible locations in order of likelihood
$possiblePaths = [
// Most common: prompt field in data.data array
['data', 'data', 'prompt'],
['data', 'data', 0, 'prompt'],
// Direct lyrics fields
['lyrics'],
['lyric'],
['text'],
// In data object
['data', 'lyrics'],
['data', 'lyric'],
['data', 'text'],
// In nested data.data
['data', 'data', 'lyrics'],
['data', 'data', 'lyric'],
['data', 'data', 'text'],
];
// First, check if we have lyrics in the prompt field (this is where API.Box actually sends them)
// IMPORTANT: Check ALL items in the data array, not just the first one
// Sometimes the first item is instrumental but later items have lyrics
if (isset($data['data']['data']) && is_array($data['data']['data'])) {
foreach ($data['data']['data'] as $item) {
// Check prompt first (most common location)
if (isset($item['prompt']) && !empty($item['prompt'])) {
$promptValue = trim($item['prompt']);
// Skip if it's just an instrumental marker
if (strtolower($promptValue) !== '[instrumental]' &&
strtolower($promptValue) !== 'instrumental' &&
strtolower($promptValue) !== '[inst]' &&
strtolower($promptValue) !== 'inst' &&
strlen($promptValue) > 3) {
$lyrics = $item['prompt'];
break; // Found valid lyrics, stop searching
}
}
}
// If no lyrics found in prompt, check other fields in all items
if (!$lyrics) {
foreach ($data['data']['data'] as $item) {
if (isset($item['lyrics']) && !empty($item['lyrics'])) {
$lyrics = $item['lyrics'];
break;
} elseif (isset($item['lyric']) && !empty($item['lyric'])) {
$lyrics = $item['lyric'];
break;
} elseif (isset($item['text']) && !empty($item['text'])) {
$lyrics = $item['text'];
break;
}
}
}
}
// Fallback to other possible locations if not found yet
if (!$lyrics) {
foreach ($possiblePaths as $path) {
$value = $data;
foreach ($path as $key) {
if (isset($value[$key])) {
$value = $value[$key];
} else {
$value = null;
break;
}
}
if ($value && !empty($value) && is_string($value)) {
$lyrics = $value;
break;
}
}
}
// Clean up lyrics if found
if ($lyrics) {
// Check if it's just an instrumental marker
$trimmed = trim($lyrics);
if (strtolower($trimmed) === '[instrumental]' ||
strtolower($trimmed) === 'instrumental' ||
strtolower($trimmed) === '[inst]' ||
strtolower($trimmed) === 'inst') {
return ''; // Don't save instrumental markers as lyrics
}
// Remove common API formatting tags but keep the content
$lyrics = str_replace(['[Verse]', '[Chorus]', '[Bridge]', '[Outro]', '[Intro]', '[Prechorus]', '[Verse 2]'], "\n", $lyrics);
$lyrics = preg_replace('/\[.*?\]/', '', $lyrics); // Remove any remaining [bracketed] content
$lyrics = preg_replace('/\n{3,}/', "\n\n", $lyrics); // Remove excessive newlines
$lyrics = trim($lyrics);
// If after cleanup it's empty or just whitespace, return empty
if (empty($lyrics) || strlen(trim($lyrics)) < 3) {
return '';
}
}
return $lyrics;
}
echo "<!DOCTYPE html>
<html>
<head>
<title>Backfill Track Lyrics</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; background: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; }
h1 { color: #333; }
.track-item { padding: 15px; margin: 10px 0; border: 1px solid #ddd; border-radius: 5px; background: #fafafa; }
.success { color: green; font-weight: bold; }
.error { color: red; font-weight: bold; }
.info { color: #666; }
.stats { background: #e8f4f8; padding: 15px; border-radius: 5px; margin: 20px 0; }
.stats h2 { margin-top: 0; }
.lyrics-preview { background: #f8f9fa; padding: 10px; border-radius: 5px; margin: 10px 0; max-height: 150px; overflow-y: auto; font-size: 0.9em; line-height: 1.6; }
</style>
</head>
<body>
<div class='container'>
<h1>đ Backfill Track Lyrics</h1>
<p>This script will extract and save lyrics from task_results JSON files for tracks missing lyrics.</p>
";
// Check if force_all is requested
$forceAll = isset($_GET['force_all']) && $_GET['force_all'] == '1';
// Get all tracks missing lyrics
$stmt = $pdo->prepare("
SELECT
id,
task_id,
title,
lyrics,
metadata,
status
FROM music_tracks
WHERE status = 'complete'
" . ($forceAll ? "" : "AND (lyrics IS NULL OR lyrics = '' OR lyrics = 'null' OR lyrics = 'NULL')") . "
ORDER BY created_at DESC
");
$stmt->execute();
$tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stats = [
'total' => count($tracks),
'processed' => 0,
'found' => 0,
'saved' => 0,
'skipped' => 0,
'failed' => 0,
'not_found' => 0
];
echo "<div class='stats'>
<h2>Statistics</h2>
<p>Total tracks to process: <strong>{$stats['total']}</strong></p>
" . ($forceAll ? "<p class='info'><strong>Mode:</strong> Force All (checking all tracks)</p>" : "<p class='info'><strong>Mode:</strong> Missing Lyrics Only</p>") . "
</div>";
foreach ($tracks as $track) {
$trackId = $track['id'];
$taskId = $track['task_id'];
$title = htmlspecialchars($track['title'] ?: 'Untitled Track');
$currentLyrics = $track['lyrics'];
$metadata = $track['metadata'];
echo "<div class='track-item'>";
echo "<h3>Track #{$trackId}: {$title}</h3>";
echo "<p class='info'>Task ID: {$taskId}</p>";
// Check if already has lyrics
$hasLyrics = false;
if (!empty($currentLyrics) && $currentLyrics !== 'null' && $currentLyrics !== 'NULL') {
if (!$forceAll) {
echo "<p class='success'>â
Already has lyrics</p>";
$hasLyrics = true;
$stats['skipped']++;
} else {
echo "<p class='info'>âšī¸ Has lyrics (length: " . strlen($currentLyrics) . " chars), but force_all is enabled</p>";
}
}
$lyricsToSave = null;
$lyricsSource = '';
// Try to extract lyrics from task_results JSON file
if (!$hasLyrics || $forceAll) {
$taskResultFile = "task_results/{$taskId}.json";
if (file_exists($taskResultFile)) {
echo "<p class='info'>đ Checking task_results JSON file...</p>";
$taskResultContent = @file_get_contents($taskResultFile);
if ($taskResultContent) {
$taskResultData = @json_decode($taskResultContent, true);
if (is_array($taskResultData)) {
$lyricsToSave = extractLyricsFromData($taskResultData);
if ($lyricsToSave) {
$lyricsSource = 'task_results JSON';
echo "<p class='success'>â
Found lyrics in task_results JSON file</p>";
} else {
echo "<p class='info'>âšī¸ No lyrics found in task_results JSON file</p>";
}
} else {
echo "<p class='error'>â ī¸ Failed to parse task_results JSON file</p>";
}
} else {
echo "<p class='error'>â ī¸ Failed to read task_results JSON file</p>";
}
} else {
echo "<p class='info'>âšī¸ task_results JSON file not found: {$taskResultFile}</p>";
}
// Also check metadata if not found in task_results
// This is important because "text" callbacks with lyrics might have been overwritten by "first" or "complete" callbacks
if (!$lyricsToSave && !empty($metadata)) {
echo "<p class='info'>đ Checking metadata (may contain lyrics from 'text' callbacks)...</p>";
$metadataArray = json_decode($metadata, true);
if (is_array($metadataArray)) {
// Check raw_callback in metadata (this contains the full callback data)
if (isset($metadataArray['raw_callback']) && is_array($metadataArray['raw_callback'])) {
$lyricsFromMetadata = extractLyricsFromData($metadataArray['raw_callback']);
if ($lyricsFromMetadata) {
$lyricsToSave = $lyricsFromMetadata;
$lyricsSource = 'metadata.raw_callback';
echo "<p class='success'>â
Found lyrics in metadata.raw_callback</p>";
}
}
// Also check if there are multiple raw_callbacks (from different callback types)
// Some metadata might store multiple callbacks
if (!$lyricsToSave) {
foreach ($metadataArray as $key => $value) {
if (is_array($value) && isset($value['data']['callbackType'])) {
// This might be a stored callback
$callbackLyrics = extractLyricsFromData($value);
if ($callbackLyrics && (!$lyricsToSave || strlen($callbackLyrics) > strlen($lyricsToSave))) {
// Prefer longer lyrics (more complete)
$lyricsToSave = $callbackLyrics;
$lyricsSource = "metadata.{$key}";
echo "<p class='success'>â
Found lyrics in metadata.{$key}</p>";
}
}
}
}
// Check for lyrics stored directly in metadata
if (!$lyricsToSave) {
$possibleLyricsFields = ['lyrics', 'lyric', 'text', 'prompt'];
foreach ($possibleLyricsFields as $field) {
if (isset($metadataArray[$field]) && !empty($metadataArray[$field]) && is_string($metadataArray[$field])) {
$testLyrics = trim($metadataArray[$field]);
if (strlen($testLyrics) > 3 &&
strtolower($testLyrics) !== '[instrumental]' &&
strtolower($testLyrics) !== 'instrumental') {
$lyricsToSave = $testLyrics;
$lyricsSource = "metadata.{$field}";
echo "<p class='success'>â
Found lyrics in metadata.{$field}</p>";
break;
}
}
}
}
}
}
}
// Save lyrics to database
if ($lyricsToSave && !empty($lyricsToSave) && strlen(trim($lyricsToSave)) >= 3) {
$stats['found']++;
// Show preview
$preview = htmlspecialchars(substr($lyricsToSave, 0, 200));
if (strlen($lyricsToSave) > 200) {
$preview .= '...';
}
echo "<div class='lyrics-preview'><strong>Lyrics Preview:</strong><br>{$preview}</div>";
echo "<p class='info'>đ Source: {$lyricsSource} | Length: " . strlen($lyricsToSave) . " characters</p>";
// Update database
$updateStmt = $pdo->prepare("UPDATE music_tracks SET lyrics = ? WHERE id = ?");
if ($updateStmt->execute([$lyricsToSave, $trackId])) {
echo "<p class='success'>â
Lyrics saved to database</p>";
$stats['saved']++;
} else {
echo "<p class='error'>â Failed to save lyrics to database</p>";
$stats['failed']++;
}
} elseif ($lyricsToSave && (empty($lyricsToSave) || strlen(trim($lyricsToSave)) < 3)) {
echo "<p class='info'>âšī¸ Found lyrics but it's too short or just an instrumental marker - skipping</p>";
$stats['not_found']++;
} elseif (!$hasLyrics) {
echo "<p class='error'>â No lyrics found in task_results or metadata</p>";
$stats['not_found']++;
}
$stats['processed']++;
echo "</div>";
// Flush output for real-time progress
if (ob_get_level() > 0) {
ob_flush();
}
flush();
}
echo "<div class='stats'>
<h2>Final Statistics</h2>
<p><strong>Total tracks:</strong> {$stats['total']}</p>
<p><strong>Processed:</strong> {$stats['processed']}</p>
<p class='success'><strong>Lyrics found:</strong> {$stats['found']}</p>
<p class='success'><strong>Lyrics saved:</strong> {$stats['saved']}</p>
<p class='info'><strong>Skipped (already has lyrics):</strong> {$stats['skipped']}</p>
<p class='error'><strong>Not found:</strong> {$stats['not_found']}</p>
<p class='error'><strong>Failed to save:</strong> {$stats['failed']}</p>
</div>";
echo "<p><a href='audit_tracks.php' style='background: #6c757d; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;'>â Back to Audit</a></p>";
echo "</div></body></html>";
?>