T.ME/BIBIL_0DAY
CasperSecurity


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/private_html/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/private_html/fix_stuck_processing_tracks.php
<?php
/**
 * Fix Stuck Processing Tracks
 * 
 * Finds tracks that are "processing" in the database but "complete" on the API
 * and processes them like a callback would (downloads audio, extracts metadata, etc.)
 */

error_reporting(E_ALL);
ini_set('display_errors', 1);

require_once 'config/database.php';

// Include callback functions - define constant to allow inclusion
define('CALLBACK_INCLUDED', true);
require_once 'callback.php';

$api_key = '63edba40620216c5aa2c04240ac41dbd';
$logFile = 'fix_stuck_tracks_log.txt';

echo "<h1>🔧 Fix Stuck Processing Tracks</h1>";
echo "<p>This script finds tracks stuck in 'processing' status and processes them</p>";

$pdo = getDBConnection();
if (!$pdo) {
    die("<p style='color: red;'>❌ Database connection failed</p>");
}

// Get all tracks with status='processing' that have a valid task_id
// Also check for tracks that might have been stuck for a while
$stmt = $pdo->prepare("
    SELECT id, task_id, title, user_id, prompt, created_at, status
    FROM music_tracks 
    WHERE status = 'processing' 
    AND task_id IS NOT NULL 
    AND task_id != '' 
    AND task_id != 'unknown'
    AND task_id NOT LIKE 'temp_%'
    AND task_id NOT LIKE 'retry_%'
    ORDER BY created_at DESC
    LIMIT 200
");

$stmt->execute();
$stuckTracks = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Also check for tracks that are processing for more than 10 minutes (likely stuck)
$stmtOld = $pdo->prepare("
    SELECT id, task_id, title, user_id, prompt, created_at, status
    FROM music_tracks 
    WHERE status = 'processing' 
    AND task_id IS NOT NULL 
    AND task_id != '' 
    AND task_id != 'unknown'
    AND task_id NOT LIKE 'temp_%'
    AND task_id NOT LIKE 'retry_%'
    AND created_at < DATE_SUB(NOW(), INTERVAL 10 MINUTE)
    ORDER BY created_at DESC
    LIMIT 200
");

$stmtOld->execute();
$oldStuckTracks = $stmtOld->fetchAll(PDO::FETCH_ASSOC);

// Merge and deduplicate by id
$allStuckTracks = [];
$seenIds = [];
foreach (array_merge($stuckTracks, $oldStuckTracks) as $track) {
    if (!in_array($track['id'], $seenIds)) {
        $allStuckTracks[] = $track;
        $seenIds[] = $track['id'];
    }
}

$stuckTracks = $allStuckTracks;

// Also check for tracks that are complete but missing variations
$stmtMissingVars = $pdo->prepare("
    SELECT mt.id, mt.task_id, mt.title, mt.user_id, mt.created_at, mt.variations_count,
           (SELECT COUNT(*) FROM audio_variations WHERE track_id = mt.id) as actual_variations_count
    FROM music_tracks mt
    WHERE mt.status = 'complete'
    AND mt.task_id IS NOT NULL 
    AND mt.task_id != '' 
    AND mt.task_id != 'unknown'
    AND mt.task_id NOT LIKE 'temp_%'
    AND mt.task_id NOT LIKE 'retry_%'
    AND (
        (SELECT COUNT(*) FROM audio_variations WHERE track_id = mt.id) = 0
        OR mt.variations_count = 0
    )
    ORDER BY mt.created_at DESC
    LIMIT 200
");

$stmtMissingVars->execute();
$tracksMissingVariations = $stmtMissingVars->fetchAll(PDO::FETCH_ASSOC);

if (empty($stuckTracks) && empty($tracksMissingVariations)) {
    echo "<p style='color: green;'>✅ No stuck tracks found!</p>";
    echo "<p>Checked for tracks with status='processing' that have valid task_ids.</p>";
    
    // Show some stats
    $statsStmt = $pdo->query("
        SELECT 
            COUNT(*) as total_processing,
            COUNT(CASE WHEN created_at < DATE_SUB(NOW(), INTERVAL 10 MINUTE) THEN 1 END) as old_processing
        FROM music_tracks 
        WHERE status = 'processing'
    ");
    $stats = $statsStmt->fetch(PDO::FETCH_ASSOC);
    
    echo "<p><strong>Stats:</strong></p>";
    echo "<ul>";
    echo "<li>Total processing tracks: " . $stats['total_processing'] . "</li>";
    echo "<li>Processing > 10 minutes: " . $stats['old_processing'] . "</li>";
    echo "</ul>";
    
    exit;
}

// If we have tracks missing variations, add them to process
if (!empty($tracksMissingVariations)) {
    echo "<h2>đŸŽĩ Tracks Missing Variations</h2>";
    echo "<p>Found " . count($tracksMissingVariations) . " complete track(s) that may be missing variations</p>";
    echo "<hr>";
    
    // Merge with stuck tracks
    $allTracksToProcess = array_merge($stuckTracks, $tracksMissingVariations);
    $stuckTracks = $allTracksToProcess;
}

echo "<p>Found " . count($stuckTracks) . " track(s) to check</p>";
if (!empty($tracksMissingVariations)) {
    echo "<p style='color: orange;'>âš ī¸ " . count($tracksMissingVariations) . " of these are complete but may be missing variations</p>";
}
echo "<hr>";

$fixedCount = 0;
$stillProcessingCount = 0;
$failedCount = 0;
$errorCount = 0;

foreach ($stuckTracks as $track) {
    $trackId = $track['id'];
    $taskId = $track['task_id'];
    $title = $track['title'];
    $currentStatus = $track['status'] ?? 'processing';
    $hasVariations = isset($track['actual_variations_count']) ? ($track['actual_variations_count'] > 0) : false;
    $variationsCount = $track['variations_count'] ?? 0;
    
    echo "<div style='border: 1px solid #ccc; padding: 15px; margin: 10px 0;'>";
    echo "<h3>Track #{$trackId}: " . htmlspecialchars($title) . "</h3>";
    echo "<p><strong>Task ID:</strong> {$taskId}</p>";
    echo "<p><strong>Status:</strong> {$currentStatus}</p>";
    echo "<p><strong>Created:</strong> {$track['created_at']}</p>";
    
    // If track is complete but missing variations, check task_results first
    if ($currentStatus === 'complete' && !$hasVariations) {
        echo "<p style='color: orange;'>âš ī¸ Track is complete but has no variations - checking task_results...</p>";
        
        $taskResultFile = "task_results/{$taskId}.json";
        if (file_exists($taskResultFile)) {
            echo "<p style='color: green;'>✅ Found task_results JSON file</p>";
            
            $taskResultContent = file_get_contents($taskResultFile);
            $taskResultData = json_decode($taskResultContent, true);
            
            if ($taskResultData && isset($taskResultData['data']['data']) && is_array($taskResultData['data']['data']) && count($taskResultData['data']['data']) > 1) {
                echo "<p>Found " . count($taskResultData['data']['data']) . " items in task_results - processing variations...</p>";
                
                // Process variations (same code as 404 case)
                $verifyStmt = $pdo->prepare("SELECT id, task_id FROM music_tracks WHERE task_id = ? OR id = ?");
                $verifyStmt->execute([$taskId, $trackId]);
                $verifyTrack = $verifyStmt->fetch(PDO::FETCH_ASSOC);
                
                if ($verifyTrack) {
                    $trackIdForVariations = $verifyTrack['id'];
                    $actualTaskId = $verifyTrack['task_id'] ?: $taskId;
                    
                    // Clear existing variations
                    $clearStmt = $pdo->prepare("DELETE FROM audio_variations WHERE track_id = ?");
                    $clearStmt->execute([$trackIdForVariations]);
                    
                    $storedVariationsCount = 0;
                    $variationIndex = 0;
                    
                    foreach ($taskResultData['data']['data'] as $originalIndex => $variation) {
                        $variationAudioUrl = (!empty($variation['audio_url'])) ? $variation['audio_url'] : 
                                            ((!empty($variation['source_audio_url'])) ? $variation['source_audio_url'] : 
                                            ((!empty($variation['stream_audio_url'])) ? $variation['stream_audio_url'] : null));
                        
                        if (!empty($variationAudioUrl)) {
                            $localVariationUrl = downloadAndStoreAudio($variationAudioUrl, $actualTaskId, 'variation', $variationIndex);
                            
                            $variationTitle = $variation['title'] ?? "AI Variation " . ($variationIndex + 1);
                            
                            $variationMetadata = [
                                'genre' => $variation['genre'] ?? null,
                                'style' => $variation['style'] ?? null,
                                'bpm' => $variation['bpm'] ?? $variation['tempo'] ?? null,
                                'key' => $variation['key'] ?? null,
                                'mood' => $variation['mood'] ?? null,
                                'energy' => $variation['energy'] ?? null,
                                'tags' => $variation['tags'] ?? null,
                                'duration' => $variation['duration'] ?? null,
                                'title' => $variationTitle
                            ];
                            
                            $variationImageUrl = null;
                            if (isset($variation['image_url']) && !empty($variation['image_url'])) {
                                $variationImageUrl = downloadAndStoreImage($variation['image_url'], $actualTaskId . '_var' . $variationIndex);
                            }
                            
                            try {
                                $variationStmt = $pdo->prepare("
                                    INSERT INTO audio_variations 
                                    (track_id, variation_index, audio_url, duration, title, tags, image_url, source_audio_url, stream_audio_url, metadata) 
                                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                                ");
                                
                                $variationTags = is_array($variation['tags']) ? implode(', ', $variation['tags']) : ($variation['tags'] ?? '');
                                
                                $insertResult = $variationStmt->execute([
                                    $trackIdForVariations,
                                    $variationIndex,
                                    $localVariationUrl ?: $variationAudioUrl,
                                    $variation['duration'] ?? null,
                                    $variationTitle,
                                    $variationTags ?: null,
                                    $variationImageUrl,
                                    $variation['source_audio_url'] ?? null,
                                    $variation['stream_audio_url'] ?? null,
                                    json_encode($variationMetadata)
                                ]);
                                
                                if ($insertResult) {
                                    $storedVariationsCount++;
                                    $variationIndex++;
                                    echo "<p style='color: green;'>✅ Stored variation {$variationIndex}: {$variationTitle}</p>";
                                } else {
                                    $errorInfo = $variationStmt->errorInfo();
                                    echo "<p style='color: red;'>❌ Failed to insert variation: " . htmlspecialchars(json_encode($errorInfo)) . "</p>";
                                }
                            } catch (Exception $e) {
                                echo "<p style='color: red;'>❌ Exception: " . htmlspecialchars($e->getMessage()) . "</p>";
                            }
                        }
                    }
                    
                    if ($storedVariationsCount > 0) {
                        $updateVarCountStmt = $pdo->prepare("UPDATE music_tracks SET variations_count = ?, selected_variation = 0 WHERE id = ?");
                        $updateVarCountStmt->execute([$storedVariationsCount, $trackIdForVariations]);
                        echo "<p style='color: green; font-weight: bold;'>✅ Stored {$storedVariationsCount} variation(s) for this track!</p>";
                        $fixedCount++;
                    }
                }
                
                echo "</div>";
                continue; // Skip API check for this track since we just processed variations
            } else {
                echo "<p style='color: orange;'>âš ī¸ task_results has " . (isset($taskResultData['data']['data']) ? count($taskResultData['data']['data']) : 0) . " items (need > 1 for variations)</p>";
            }
        } else {
            echo "<p style='color: orange;'>âš ī¸ No task_results JSON file found</p>";
        }
    }
    
        // Check API status
        $api_url = "https://api.api.box/api/v1/status/{$taskId}";
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $api_url);
    curl_setopt($ch, CURLOPT_HTTPGET, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer ' . $api_key,
        'Content-Type: application/json',
        'User-Agent: SoundStudioPro-Fix/1.0'
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);
    
    if ($curl_error) {
        echo "<p style='color: red;'>❌ cURL Error: {$curl_error}</p>";
        $errorCount++;
        echo "</div>";
        continue;
    }
    
    if ($http_code !== 200) {
        echo "<p style='color: orange;'>âš ī¸ API returned HTTP {$http_code}</p>";
        if ($http_code === 404) {
            echo "<p>Track data not available</p>";
            echo "<p>🔍 Checking for local files or saved task results...</p>";
            
            // Check if track already has an audio_url in database
            $stmt_check = $pdo->prepare("SELECT audio_url FROM music_tracks WHERE id = ?");
            $stmt_check->execute([$trackId]);
            $existingAudioUrl = $stmt_check->fetchColumn();
            
            // Check for local audio file
            $audioExtensions = ['mp3', 'wav', 'm4a', 'ogg'];
            $localAudioUrl = null;
            foreach ($audioExtensions as $ext) {
                $localPath = "audio_files/{$taskId}.{$ext}";
                if (file_exists($localPath)) {
                    $localAudioUrl = "/audio_files/{$taskId}.{$ext}";
                    echo "<p style='color: green;'>✅ Found local audio file: {$localAudioUrl}</p>";
                    break;
                }
            }
            
            // If track has audio_url but no local file, try to download it
            if (!$localAudioUrl && $existingAudioUrl && filter_var($existingAudioUrl, FILTER_VALIDATE_URL)) {
                echo "<p>đŸ“Ĩ Track has external audio URL, attempting to download...</p>";
                $localAudioUrl = downloadAndStoreAudio($existingAudioUrl, $taskId, 'main');
                if ($localAudioUrl) {
                    echo "<p style='color: green;'>✅ Downloaded audio from existing URL</p>";
                } else {
                    echo "<p style='color: orange;'>âš ī¸ Failed to download, will use existing URL</p>";
                    $localAudioUrl = $existingAudioUrl; // Use existing URL as fallback
                }
            }
            
            // Check for task_results JSON file
            $taskResultFile = "task_results/{$taskId}.json";
            $taskResultData = null;
            if (file_exists($taskResultFile)) {
                echo "<p style='color: green;'>✅ Found task_results JSON file</p>";
                $taskResultContent = file_get_contents($taskResultFile);
                $taskResultData = json_decode($taskResultContent, true);
                
                if ($taskResultData) {
                    // Extract audio URL from task results if we don't have local file
                    if (!$localAudioUrl) {
                        if (isset($taskResultData['data']['data']) && is_array($taskResultData['data']['data'])) {
                            foreach ($taskResultData['data']['data'] as $audio) {
                                $audioUrl = (!empty($audio['audio_url'])) ? $audio['audio_url'] : 
                                           ((!empty($audio['source_audio_url'])) ? $audio['source_audio_url'] : 
                                           ((!empty($audio['stream_audio_url'])) ? $audio['stream_audio_url'] : null));
                                if ($audioUrl) {
                                    // Try to download it now
                                    $localAudioUrl = downloadAndStoreAudio($audioUrl, $taskId, 'main');
                                    if ($localAudioUrl) {
                                        echo "<p style='color: green;'>✅ Downloaded audio from task_results data</p>";
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            
            // If we have local audio or task results, mark as complete
            if ($localAudioUrl || $taskResultData) {
                echo "<p style='color: green;'>✅ Recovering track from local files/task results...</p>";
                
                // Get original track data
                $originalData = getTrackOriginalData($taskId);
                $originalPrompt = $originalData['prompt'] ?? $track['prompt'];
                
                // Process task_results JSON to extract ALL data
                $title = $track['title'];
                $duration = null;
                $tags = null;
                $modelName = null;
                $lyrics = '';
                $imageUrl = null;
                $localImageUrl = null;
                
                // Extract data from task_results JSON
                if ($taskResultData && isset($taskResultData['data']['data']) && is_array($taskResultData['data']['data'])) {
                    // Find the first item with audio_url (the complete one)
                    $completeItem = null;
                    foreach ($taskResultData['data']['data'] as $item) {
                        $itemAudioUrl = (!empty($item['audio_url'])) ? $item['audio_url'] : 
                                       ((!empty($item['source_audio_url'])) ? $item['source_audio_url'] : 
                                       ((!empty($item['stream_audio_url'])) ? $item['stream_audio_url'] : null));
                        if ($itemAudioUrl) {
                            $completeItem = $item;
                            break;
                        }
                    }
                    
                    // If no complete item, use first item
                    if (!$completeItem && !empty($taskResultData['data']['data'])) {
                        $completeItem = $taskResultData['data']['data'][0];
                    }
                    
                    if ($completeItem) {
                        // Extract title
                        if (isset($completeItem['title']) && !empty($completeItem['title'])) {
                            $title = $title ?: $completeItem['title'];
                        }
                        
                        // Extract duration
                        if (isset($completeItem['duration']) && !empty($completeItem['duration'])) {
                            $duration = floatval($completeItem['duration']);
                        }
                        
                        // Extract tags
                        if (isset($completeItem['tags']) && !empty($completeItem['tags'])) {
                            $tags = is_array($completeItem['tags']) ? $completeItem['tags'] : explode(',', $completeItem['tags']);
                            $tags = array_map('trim', $tags);
                            $tags = formatTagsForStorage($tags);
                        }
                        
                        // Extract model_name
                        if (isset($completeItem['model_name']) && !empty($completeItem['model_name'])) {
                            $modelName = $completeItem['model_name'];
                        }
                        
                        // Extract lyrics (from prompt field)
                        if (isset($completeItem['prompt']) && !empty($completeItem['prompt'])) {
                            $lyrics = $completeItem['prompt'];
                        }
                        
                        // Extract image URL
                        if (isset($completeItem['image_url']) && !empty($completeItem['image_url'])) {
                            $imageUrl = $completeItem['image_url'];
                        } elseif (isset($completeItem['source_image_url']) && !empty($completeItem['source_image_url'])) {
                            $imageUrl = $completeItem['source_image_url'];
                        }
                    }
                    
                    // Also check all items for lyrics if not found
                    if (!$lyrics) {
                        foreach ($taskResultData['data']['data'] as $item) {
                            if (isset($item['prompt']) && !empty($item['prompt']) && $item['prompt'] !== '[Instrumental]') {
                                $lyrics = $item['prompt'];
                                break;
                            }
                        }
                    }
                }
                
                // Clean up lyrics
                if ($lyrics && $lyrics !== '[Instrumental]') {
                    $lyrics = str_replace(['[Verse]', '[Chorus]', '[Bridge]', '[Outro]', '[Intro]', '[Instrumental]'], '', $lyrics);
                    $lyrics = preg_replace('/\[.*?\]/', '', $lyrics);
                    $lyrics = trim($lyrics);
                    if (empty($lyrics)) {
                        $lyrics = null;
                    }
                } else {
                    $lyrics = null;
                }
                
                // Download image if we have URL
                if ($imageUrl) {
                    $localImageUrl = downloadAndStoreImage($imageUrl, $taskId);
                    if ($localImageUrl) {
                        echo "<p style='color: green;'>✅ Downloaded image: {$localImageUrl}</p>";
                    }
                }
                
                // If we still don't have audio URL but have task results, try to get it
                if (!$localAudioUrl && $taskResultData && isset($taskResultData['data']['data'])) {
                    foreach ($taskResultData['data']['data'] as $item) {
                        $itemAudioUrl = (!empty($item['audio_url'])) ? $item['audio_url'] : 
                                       ((!empty($item['source_audio_url'])) ? $item['source_audio_url'] : 
                                       ((!empty($item['stream_audio_url'])) ? $item['stream_audio_url'] : null));
                        if ($itemAudioUrl) {
                            $localAudioUrl = downloadAndStoreAudio($itemAudioUrl, $taskId, 'main');
                            if ($localAudioUrl) {
                                echo "<p style='color: green;'>✅ Downloaded audio from task_results: {$localAudioUrl}</p>";
                                break;
                            }
                        }
                    }
                }
                
                // Build callback data structure for metadata extraction
                $callbackData = $taskResultData ?: [
                    'task_id' => $taskId,
                    'status' => 'complete',
                    'data' => []
                ];
                
                // Extract comprehensive metadata
                $enhanced_metadata = extractComprehensiveMetadata($callbackData, $originalPrompt);
                
                // Override with extracted values
                if ($tags) {
                    $enhanced_metadata['tags'] = $tags;
                }
                if ($duration) {
                    $enhanced_metadata['audio_quality']['duration'] = $duration;
                }
                if ($modelName) {
                    $enhanced_metadata['generation_parameters']['model_name'] = $modelName;
                }
                
                // Store tags in metadata
                if ($tags) {
                    $enhanced_metadata['tags'] = $tags;
                }
                
                // Display extracted data
                echo "<p><strong>Extracted Data:</strong></p>";
                echo "<ul>";
                echo "<li>Title: " . htmlspecialchars($title ?: 'N/A') . "</li>";
                echo "<li>Duration: " . ($duration ? number_format($duration, 2) . "s" : 'N/A') . "</li>";
                echo "<li>Tags: " . htmlspecialchars($tags ?: 'N/A') . "</li>";
                echo "<li>Model: " . htmlspecialchars($modelName ?: 'N/A') . "</li>";
                echo "<li>Lyrics: " . ($lyrics ? "Yes (" . strlen($lyrics) . " chars)" : 'No') . "</li>";
                echo "<li>Audio: " . ($localAudioUrl ? htmlspecialchars($localAudioUrl) : 'N/A') . "</li>";
                echo "<li>Image: " . ($localImageUrl ? htmlspecialchars($localImageUrl) : 'N/A') . "</li>";
                echo "</ul>";
                
                $metadata = json_encode($enhanced_metadata);
                
                // Verify track exists with this task_id
                $verifyStmt = $pdo->prepare("SELECT id, task_id FROM music_tracks WHERE task_id = ? OR id = ?");
                $verifyStmt->execute([$taskId, $trackId]);
                $verifyTrack = $verifyStmt->fetch(PDO::FETCH_ASSOC);
                
                if (!$verifyTrack) {
                    echo "<p style='color: red;'>❌ Track not found in database with task_id {$taskId} or id {$trackId}</p>";
                    $errorCount++;
                    echo "</div>";
                    continue;
                }
                
                // Use the actual task_id from database if different
                $actualTaskId = $verifyTrack['task_id'] ?: $taskId;
                if ($actualTaskId !== $taskId) {
                    echo "<p style='color: orange;'>âš ī¸ Task ID mismatch: DB has '{$actualTaskId}', using that instead</p>";
                }
                
                // CRITICAL: Check if there are multiple tracks with the same task_id
                // If so, we need to consolidate them - one main track, others as variations
                $duplicateTracksStmt = $pdo->prepare("
                    SELECT id, title, audio_url, image_url, duration, created_at 
                    FROM music_tracks 
                    WHERE task_id = ? AND status = 'complete'
                    ORDER BY created_at ASC, id ASC
                ");
                $duplicateTracksStmt->execute([$actualTaskId]);
                $duplicateTracks = $duplicateTracksStmt->fetchAll(PDO::FETCH_ASSOC);
                
                $storedVariationsCount = 0; // Initialize for duplicate consolidation
                $trackIdForVariations = $verifyTrack['id']; // Default to current track
                
                if (count($duplicateTracks) > 1) {
                    echo "<p style='color: orange;'>âš ī¸ Found " . count($duplicateTracks) . " tracks with same task_id - consolidating into variations...</p>";
                    
                    // First track is the main one
                    $mainTrackId = $duplicateTracks[0]['id'];
                    $otherTracks = array_slice($duplicateTracks, 1);
                    
                    echo "<p>Main track: #{$mainTrackId} ({$duplicateTracks[0]['title']})</p>";
                    echo "<p>Converting " . count($otherTracks) . " duplicate track(s) to variations...</p>";
                    
                    // Get current variation count for main track
                    $currentVarCountStmt = $pdo->prepare("SELECT COUNT(*) FROM audio_variations WHERE track_id = ?");
                    $currentVarCountStmt->execute([$mainTrackId]);
                    $storedVariationsCount = $currentVarCountStmt->fetchColumn();
                    
                    // Convert other tracks to variations
                    foreach ($otherTracks as $otherTrack) {
                        $variationIndex = $storedVariationsCount; // Use current count
                        
                        // Check if this track already exists as a variation
                        $checkVarStmt = $pdo->prepare("SELECT COUNT(*) FROM audio_variations WHERE track_id = ? AND audio_url = ?");
                        $checkVarStmt->execute([$mainTrackId, $otherTrack['audio_url']]);
                        $exists = $checkVarStmt->fetchColumn() > 0;
                        
                        if (!$exists && !empty($otherTrack['audio_url'])) {
                            try {
                                $convertStmt = $pdo->prepare("
                                    INSERT INTO audio_variations 
                                    (track_id, variation_index, audio_url, duration, title, tags, image_url, metadata) 
                                    VALUES (?, ?, ?, ?, ?, ?, ?, ?)
                                ");
                                
                                $convertMetadata = json_encode([
                                    'title' => $otherTrack['title'],
                                    'duration' => $otherTrack['duration'],
                                    'converted_from_track_id' => $otherTrack['id'],
                                    'converted_at' => date('Y-m-d H:i:s')
                                ]);
                                
                                $convertResult = $convertStmt->execute([
                                    $mainTrackId,
                                    $variationIndex,
                                    $otherTrack['audio_url'],
                                    $otherTrack['duration'],
                                    $otherTrack['title'] ?: "Variation " . ($variationIndex + 1),
                                    null,
                                    $otherTrack['image_url'],
                                    $convertMetadata
                                ]);
                                
                                if ($convertResult) {
                                    $storedVariationsCount++;
                                    echo "<p style='color: green;'>✅ Converted track #{$otherTrack['id']} ({$otherTrack['title']}) to variation #{$variationIndex}</p>";
                                    
                                    // Optionally delete the duplicate track (or mark as deleted)
                                    // $deleteStmt = $pdo->prepare("DELETE FROM music_tracks WHERE id = ?");
                                    // $deleteStmt->execute([$otherTrack['id']]);
                                }
                            } catch (Exception $e) {
                                echo "<p style='color: red;'>❌ Failed to convert track #{$otherTrack['id']}: " . htmlspecialchars($e->getMessage()) . "</p>";
                            }
                        }
                    }
                    
                    // Use main track ID for variations
                    $trackIdForVariations = $mainTrackId;
                    echo "<p style='color: green;'>✅ All variations will be linked to main track #{$mainTrackId}</p>";
                } else {
                    $trackIdForVariations = $verifyTrack['id'];
                }
                
                // Update track - try direct SQL update if updateMusicTrack fails
                $updateResult = updateMusicTrack(
                    $actualTaskId, 
                    'complete', 
                    $localAudioUrl, 
                    null, 
                    !empty($lyrics) ? $lyrics : null, 
                    $metadata, 
                    $duration, 
                    $title, 
                    $tags, 
                    $modelName, 
                    $localImageUrl
                );
                
                if ($updateResult) {
                    echo "<p style='color: green; font-weight: bold;'>✅ Successfully recovered and marked as complete!</p>";
                    
                    // Process variations from task_results
                    if ($taskResultData && isset($taskResultData['data']['data']) && is_array($taskResultData['data']['data']) && count($taskResultData['data']['data']) > 1) {
                        echo "<p>đŸŽĩ Processing variations from task_results...</p>";
                        
                        // Get the track ID (not task_id) - CRITICAL: This is the MAIN track that variations will be linked to
                        // Use $trackIdForVariations if set from duplicate consolidation, otherwise use verifyTrack id
                        if (!isset($trackIdForVariations)) {
                            $trackIdForVariations = $verifyTrack['id'];
                        }
                        
                        echo "<p><strong>🔗 Linking variations to Main Track ID:</strong> {$trackIdForVariations}</p>";
                        echo "<p><strong>Task ID:</strong> {$actualTaskId}</p>";
                        
                        // Clear existing variations for this track
                        $clearStmt = $pdo->prepare("DELETE FROM audio_variations WHERE track_id = ?");
                        $clearStmt->execute([$trackIdForVariations]);
                        $deletedCount = $clearStmt->rowCount();
                        echo "<p>Cleared {$deletedCount} existing variation(s) for track #{$trackIdForVariations}</p>";
                        
                        $storedVariationsCount = 0;
                        $variationIndex = 0;
                        
                        echo "<p>Found " . count($taskResultData['data']['data']) . " item(s) in task_results data array - processing ALL as variations...</p>";
                        
                        foreach ($taskResultData['data']['data'] as $originalIndex => $variation) {
                            // Check for audio URL (same logic as callback)
                            $variationAudioUrl = (!empty($variation['audio_url'])) ? $variation['audio_url'] : 
                                                ((!empty($variation['source_audio_url'])) ? $variation['source_audio_url'] : 
                                                ((!empty($variation['stream_audio_url'])) ? $variation['stream_audio_url'] : null));
                            
                            echo "<p>Processing item {$originalIndex}: audio_url=" . ($variation['audio_url'] ?? 'empty') . ", source=" . ($variation['source_audio_url'] ?? 'none') . ", stream=" . ($variation['stream_audio_url'] ?? 'none') . "</p>";
                            
                            if (!empty($variationAudioUrl)) {
                                // Download variation audio
                                $localVariationUrl = downloadAndStoreAudio($variationAudioUrl, $actualTaskId, 'variation', $variationIndex);
                                
                                // Extract variation title
                                $variationTitle = $variation['title'] ?? null;
                                if (!$variationTitle) {
                                    $titleParts = [];
                                    if (isset($variation['genre'])) $titleParts[] = $variation['genre'];
                                    if (isset($variation['style'])) $titleParts[] = $variation['style'];
                                    if (isset($variation['mood'])) $titleParts[] = $variation['mood'];
                                    if (isset($variation['energy'])) $titleParts[] = $variation['energy'];
                                    
                                    if (!empty($titleParts)) {
                                        $variationTitle = implode(' ', $titleParts) . ' Variation';
                                    } else {
                                        $variationTitle = "AI Variation " . ($variationIndex + 1);
                                    }
                                }
                                
                                // Build variation metadata
                                $variationMetadata = [
                                    'genre' => $variation['genre'] ?? (is_array($variation['tags']) ? $variation['tags'][0] : (is_string($variation['tags']) ? explode(',', $variation['tags'])[0] : null)),
                                    'style' => $variation['style'] ?? null,
                                    'bpm' => $variation['bpm'] ?? $variation['tempo'] ?? null,
                                    'key' => $variation['key'] ?? null,
                                    'mood' => $variation['mood'] ?? null,
                                    'energy' => $variation['energy'] ?? null,
                                    'instruments' => $variation['instruments'] ?? null,
                                    'tags' => $variation['tags'] ?? null,
                                    'duration' => $variation['duration'] ?? null,
                                    'title' => $variationTitle
                                ];
                                
                                // Download variation image if available
                                $variationImageUrl = null;
                                if (isset($variation['image_url']) && !empty($variation['image_url'])) {
                                    $variationImageUrl = downloadAndStoreImage($variation['image_url'], $actualTaskId . '_var' . $variationIndex);
                                } elseif (isset($variation['source_image_url']) && !empty($variation['source_image_url'])) {
                                    $variationImageUrl = downloadAndStoreImage($variation['source_image_url'], $actualTaskId . '_var' . $variationIndex);
                                }
                                
                                // Insert variation
                                try {
                                    $variationStmt = $pdo->prepare("
                                        INSERT INTO audio_variations 
                                        (track_id, variation_index, audio_url, duration, title, tags, image_url, source_audio_url, stream_audio_url, metadata) 
                                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                                    ");
                                    
                                    $variationTags = is_array($variation['tags']) ? implode(', ', $variation['tags']) : ($variation['tags'] ?? '');
                                    
                                    // Ensure metadata is valid JSON and not too large
                                    $variationMetadataJson = json_encode($variationMetadata);
                                    if (json_last_error() !== JSON_ERROR_NONE) {
                                        echo "<p style='color: red;'>❌ Invalid JSON in variation metadata: " . json_last_error_msg() . "</p>";
                                        $variationMetadataJson = json_encode(['title' => $variationTitle, 'duration' => $variation['duration'] ?? null]);
                                    }
                                    
                                    $insertParams = [
                                        $trackIdForVariations,
                                        $variationIndex,
                                        $localVariationUrl ?: $variationAudioUrl,
                                        $variation['duration'] ?? null,
                                        $variationTitle,
                                        $variationTags ?: null,
                                        $variationImageUrl,
                                        $variation['source_audio_url'] ?? null,
                                        $variation['stream_audio_url'] ?? null,
                                        $variationMetadataJson
                                    ];
                                    
                                    echo "<p>Inserting variation with params: track_id={$trackIdForVariations}, index={$variationIndex}, audio=" . substr($insertParams[2], 0, 50) . "...</p>";
                                    
                                    $insertResult = $variationStmt->execute($insertParams);
                                    
                                    if ($insertResult) {
                                        $storedVariationsCount++;
                                        $variationIndex++;
                                        echo "<p style='color: green;'>✅ Stored variation {$variationIndex}: {$variationTitle}</p>";
                                    } else {
                                        $errorInfo = $variationStmt->errorInfo();
                                        echo "<p style='color: red;'>❌ Failed to insert variation: " . htmlspecialchars(json_encode($errorInfo)) . "</p>";
                                        echo "<p>SQL: " . htmlspecialchars($variationStmt->queryString) . "</p>";
                                    }
                                } catch (Exception $e) {
                                    echo "<p style='color: red;'>❌ Exception inserting variation: " . htmlspecialchars($e->getMessage()) . "</p>";
                                    echo "<p>Trace: " . htmlspecialchars($e->getTraceAsString()) . "</p>";
                                }
                            } else {
                                echo "<p style='color: orange;'>âš ī¸ Skipping variation {$originalIndex} - no audio URL</p>";
                            }
                        }
                        
                        // Update variations_count on main track - CRITICAL for variation button to show
                        if ($storedVariationsCount > 0) {
                            $updateVarCountStmt = $pdo->prepare("UPDATE music_tracks SET variations_count = ?, selected_variation = 0 WHERE id = ?");
                            $updateVarCountStmt->execute([$storedVariationsCount, $trackIdForVariations]);
                            
                            // Verify the update
                            $verifyCountStmt = $pdo->prepare("SELECT variations_count FROM music_tracks WHERE id = ?");
                            $verifyCountStmt->execute([$trackIdForVariations]);
                            $updatedCount = $verifyCountStmt->fetchColumn();
                            
                            echo "<p style='color: green; font-weight: bold;'>✅ Stored {$storedVariationsCount} variation(s) and updated variations_count to {$updatedCount}</p>";
                            
                            // Double-check: count actual variations in database
                            $actualCountStmt = $pdo->prepare("SELECT COUNT(*) FROM audio_variations WHERE track_id = ?");
                            $actualCountStmt->execute([$trackIdForVariations]);
                            $actualCount = $actualCountStmt->fetchColumn();
                            
                            echo "<p>Verified: {$actualCount} variation(s) actually stored in audio_variations table for track #{$trackIdForVariations}</p>";
                            
                            if ($actualCount != $storedVariationsCount) {
                                echo "<p style='color: red;'>âš ī¸ WARNING: Count mismatch! Expected {$storedVariationsCount} but found {$actualCount} in database</p>";
                            }
                        } else {
                            echo "<p style='color: orange;'>âš ī¸ No variations were stored (all items may have had empty audio URLs)</p>";
                        }
                    }
                    
                    $fixedCount++;
                    
                    $logEntry = "✅ Recovered track #{$trackId} from local files/task_results (404 on API)\n";
                    file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
                } else {
                    // Try direct SQL update as fallback
                    echo "<p style='color: orange;'>âš ī¸ updateMusicTrack failed, trying direct SQL update...</p>";
                    
                    try {
                        $directUpdates = ['status = ?'];
                        $directParams = ['complete'];
                        
                        if ($localAudioUrl) {
                            $directUpdates[] = 'audio_url = ?';
                            $directParams[] = $localAudioUrl;
                        }
                        
                        if ($metadata) {
                            $directUpdates[] = 'metadata = ?';
                            $directParams[] = $metadata;
                        }
                        
                        if ($duration !== null) {
                            $directUpdates[] = 'duration = ?';
                            $directParams[] = $duration;
                        }
                        
                        if (!empty($lyrics)) {
                            $directUpdates[] = 'lyrics = ?';
                            $directParams[] = $lyrics;
                        }
                        
                        if ($tags) {
                            $directUpdates[] = 'tags = ?';
                            $directParams[] = is_array($tags) ? implode('; ', $tags) : $tags;
                        }
                        
                        if ($localImageUrl) {
                            $directUpdates[] = 'image_url = ?';
                            $directParams[] = $localImageUrl;
                        }
                        
                        $directUpdates[] = 'updated_at = NOW()';
                        $directParams[] = $actualTaskId;
                        
                        $directSql = "UPDATE music_tracks SET " . implode(', ', $directUpdates) . " WHERE task_id = ?";
                        $directStmt = $pdo->prepare($directSql);
                        $directResult = $directStmt->execute($directParams);
                        
                        if ($directResult) {
                            $rowsAffected = $directStmt->rowCount();
                            if ($rowsAffected > 0) {
                                echo "<p style='color: green; font-weight: bold;'>✅ Successfully recovered via direct SQL update!</p>";
                                $fixedCount++;
                                
                                $logEntry = "✅ Recovered track #{$trackId} via direct SQL (404 on API)\n";
                                file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
                            } else {
                                echo "<p style='color: red;'>❌ Direct SQL update executed but no rows affected (task_id may not match)</p>";
                                echo "<p>Debug: task_id = {$taskId}, track_id = {$trackId}</p>";
                                
                                // Try updating by track ID instead
                                $directParams[count($directParams) - 1] = $trackId; // Replace last param (task_id) with track id
                                $directSqlById = "UPDATE music_tracks SET " . implode(', ', $directUpdates) . " WHERE id = ?";
                                $directStmtById = $pdo->prepare($directSqlById);
                                $directResultById = $directStmtById->execute($directParams);
                                
                                if ($directResultById && $directStmtById->rowCount() > 0) {
                                    echo "<p style='color: green; font-weight: bold;'>✅ Successfully recovered via track ID update!</p>";
                                    $fixedCount++;
                                } else {
                                    echo "<p style='color: red;'>❌ Failed to update by track ID as well</p>";
                                    $errorInfo = $directStmtById->errorInfo();
                                    echo "<p>SQL Error: " . htmlspecialchars(json_encode($errorInfo)) . "</p>";
                                    $errorCount++;
                                }
                            }
                        } else {
                            $errorInfo = $directStmt->errorInfo();
                            echo "<p style='color: red;'>❌ Direct SQL update failed</p>";
                            echo "<p>SQL Error: " . htmlspecialchars(json_encode($errorInfo)) . "</p>";
                            echo "<p>SQL: " . htmlspecialchars($directSql) . "</p>";
                            $errorCount++;
                        }
                    } catch (Exception $e) {
                        echo "<p style='color: red;'>❌ Exception during direct SQL update: " . htmlspecialchars($e->getMessage()) . "</p>";
                        $errorCount++;
                    }
                }
            } else {
                // No local files or task results - mark as failed and refund
                echo "<p style='color: red;'>❌ No local files or task results found - marking as failed</p>";
                
                $error_metadata = json_encode([
                    'code' => 404,
                    'msg' => 'Track generation incomplete',
                    'error_type' => 'not_found',
                    'timestamp' => date('Y-m-d H:i:s'),
                    'fixed_by' => 'fix_stuck_processing_tracks'
                ]);
                
                $update_stmt = $pdo->prepare("UPDATE music_tracks SET status = 'failed', metadata = ?, updated_at = NOW() WHERE id = ?");
                if ($update_stmt->execute([$error_metadata, $trackId])) {
                    // Refund credit
                    $refund_stmt = $pdo->prepare("UPDATE users SET credits = credits + 1 WHERE id = ?");
                    $refund_stmt->execute([$track['user_id']]);
                    
                    // Record refund transaction
                    $check_stmt = $pdo->prepare("SELECT COUNT(*) FROM credit_transactions WHERE user_id = ? AND type = 'refund' AND description LIKE ?");
                    $check_stmt->execute([$track['user_id'], "%Track #{$trackId}%"]);
                    if ($check_stmt->fetchColumn() == 0) {
                        $trans_stmt = $pdo->prepare("
                            INSERT INTO credit_transactions (user_id, amount, type, description, created_at) 
                            VALUES (?, 1, 'refund', 'Track #{$trackId} incomplete - refunded', NOW())
                        ");
                        $trans_stmt->execute([$track['user_id']]);
                    }
                    
                    echo "<p style='color: green;'>✅ Marked as failed and refunded 1 credit</p>";
                    $failedCount++;
                } else {
                    echo "<p style='color: red;'>❌ Failed to update database</p>";
                    $errorCount++;
                }
            }
        } else {
            $errorCount++;
        }
        echo "</div>";
        continue;
    }
    
    $api_data = json_decode($response, true);
    
    if (!$api_data) {
        echo "<p style='color: red;'>❌ Invalid JSON response from API</p>";
        $errorCount++;
        echo "</div>";
        continue;
    }
    
    // Check for error codes
    if (isset($api_data['code']) && ($api_data['code'] == 400 || $api_data['code'] == 531 || $api_data['code'] >= 400)) {
        echo "<p style='color: red;'>❌ API returned error code: {$api_data['code']}</p>";
        
        // Sanitize error message - remove API.Box references only
        $api_error_msg = $api_data['msg'] ?? 'Generation failed';
        $sanitized_error = preg_replace('/\b(API\.Box|api\.box|API\.box|not found on API\.Box|not found in API\.Box|Task not found in API\.Box|Track.*not found on API\.Box)\b/i', '', $api_error_msg);
        $sanitized_error = trim($sanitized_error);
        $sanitized_error = preg_replace('/\s+/', ' ', $sanitized_error);
        if (empty($sanitized_error)) {
            $sanitized_error = 'Generation failed';
        }
        
        echo "<p><strong>Error:</strong> " . htmlspecialchars($sanitized_error) . "</p>";
        
        // Mark as failed
        $error_metadata = json_encode([
            'code' => $api_data['code'],
            'msg' => $sanitized_error,
            'error_type' => ($api_data['code'] == 400) ? 'content_violation' : 'generation_failed',
            'data' => $api_data,
            'timestamp' => date('Y-m-d H:i:s'),
            'fixed_by' => 'fix_stuck_processing_tracks'
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        
        $update_stmt = $pdo->prepare("UPDATE music_tracks SET status = 'failed', metadata = ?, updated_at = NOW() WHERE id = ?");
        if ($update_stmt->execute([$error_metadata, $trackId])) {
            echo "<p style='color: green;'>✅ Updated track to 'failed' status</p>";
            
            // Check if credit was already refunded
            $check_stmt = $pdo->prepare("
                SELECT COUNT(*) 
                FROM credit_transactions 
                WHERE user_id = ? 
                AND type = 'refund' 
                AND (description LIKE ? OR description LIKE ? OR description LIKE ?)
            ");
            $check_stmt->execute([
                $track['user_id'],
                "%Track failed: {$trackId}%",
                "%Track #{$trackId}%",
                "%track {$trackId}%"
            ]);
            $already_refunded = $check_stmt->fetchColumn() > 0;
            
            if (!$already_refunded) {
                // Refund credit
                $refund_stmt = $pdo->prepare("UPDATE users SET credits = credits + 1 WHERE id = ?");
                if ($refund_stmt->execute([$track['user_id']])) {
                    // Record refund transaction
                    $trans_stmt = $pdo->prepare("
                        INSERT INTO credit_transactions (user_id, amount, type, description, created_at) 
                        VALUES (?, 1, 'refund', 'Track failed (stuck fix): {$trackId} - {$track['title']}', NOW())
                    ");
                    $trans_stmt->execute([$track['user_id']]);
                    echo "<p style='color: green;'>✅ Refunded 1 credit</p>";
                }
            } else {
                echo "<p style='color: blue;'>â„šī¸ Credit already refunded</p>";
            }
            
            $failedCount++;
        } else {
            echo "<p style='color: red;'>❌ Failed to update database</p>";
        }
        echo "</div>";
        continue;
    }
    
    // Get API status
    $api_status = $api_data['status'] ?? null;
    
    // Check if it's complete
    if ($api_status === 'complete' || $api_status === 'completed' || 
        (isset($api_data['data']['callbackType']) && $api_data['data']['callbackType'] === 'complete')) {
        
        echo "<p style='color: green;'>✅ Track is COMPLETE - processing...</p>";
        
        // Simulate callback processing
        // We need to format the API response like a callback would be
        $callbackData = $api_data;
        
        // If the API response has a different structure, normalize it
        if (isset($api_data['data']) && !isset($callbackData['task_id'])) {
            $callbackData['task_id'] = $taskId;
            $callbackData['status'] = 'complete';
        }
        
        // Log the fix attempt
        $logEntry = "[" . date('Y-m-d H:i:s') . "] Fixing stuck track #{$trackId} (task_id: {$taskId})\n";
        $logEntry .= "API Status: complete\n";
        file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
        
        // Process like a callback - we'll use the callback processing logic
        // But we need to call it in a way that works
        
        // Get original track data for metadata extraction
        $originalData = getTrackOriginalData($taskId);
        $originalPrompt = $originalData['prompt'] ?? $track['prompt'];
        
        // Extract comprehensive metadata
        $enhanced_metadata = extractComprehensiveMetadata($callbackData, $originalPrompt);
        
        // Extract title, duration, tags, model_name, lyrics
        $apiTitle = extractTitleFromCallback($callbackData);
        $title = $track['title'] ?: $apiTitle;
        
        $duration = extractDurationFromCallback($callbackData);
        $tagsRaw = extractTagsFromCallback($callbackData);
        $tags = formatTagsForStorage($tagsRaw);
        $modelName = extractModelNameFromCallback($callbackData);
        
        // Extract lyrics
        $lyrics = '';
        if (isset($callbackData['data']['data']) && is_array($callbackData['data']['data'])) {
            foreach ($callbackData['data']['data'] as $item) {
                if (isset($item['prompt']) && !empty($item['prompt'])) {
                    $lyrics = $item['prompt'];
                    break;
                }
            }
        }
        
        if ($lyrics) {
            $lyrics = str_replace(['[Verse]', '[Chorus]', '[Bridge]', '[Outro]', '[Intro]'], '', $lyrics);
            $lyrics = preg_replace('/\[.*?\]/', '', $lyrics);
            $lyrics = trim($lyrics);
        }
        
        // Extract and download image
        $imageUrl = extractImageUrlFromCallback($callbackData);
        $localImageUrl = null;
        if ($imageUrl) {
            $localImageUrl = downloadAndStoreImage($imageUrl, $taskId);
        }
        
        // Extract audio URL - check multiple possible locations
        $audioUrl = null;
        if (isset($callbackData['data']['data']) && is_array($callbackData['data']['data'])) {
            foreach ($callbackData['data']['data'] as $audio) {
                $audioUrl = (!empty($audio['audio_url'])) ? $audio['audio_url'] : 
                           ((!empty($audio['source_audio_url'])) ? $audio['source_audio_url'] : 
                           ((!empty($audio['stream_audio_url'])) ? $audio['stream_audio_url'] : null));
                if ($audioUrl) break;
            }
        } else {
            $audioUrl = $callbackData['audio_url'] ?? 
                       $callbackData['result']['audio_url'] ?? 
                       $callbackData['data']['audio_url'] ?? null;
        }
        
        $localAudioUrl = null;
        if ($audioUrl) {
            $localAudioUrl = downloadAndStoreAudio($audioUrl, $taskId, 'main');
        }
        
        // Store tags in metadata
        if ($tags) {
            $enhanced_metadata['tags'] = $tags;
        }
        
        $metadata = json_encode($enhanced_metadata);
        
        // Update the track
        $updateResult = updateMusicTrack(
            $taskId, 
            'complete', 
            $localAudioUrl ?: $audioUrl, 
            null, 
            !empty($lyrics) ? $lyrics : null, 
            $metadata, 
            $duration, 
            $title, 
            $tags, 
            $modelName, 
            $localImageUrl
        );
        
            if ($updateResult) {
                echo "<p style='color: green;'>✅ Successfully updated track to 'complete'!</p>";
                if ($localAudioUrl) {
                    echo "<p>✅ Audio downloaded locally: {$localAudioUrl}</p>";
                } elseif ($audioUrl) {
                    echo "<p>âš ī¸ Using external audio URL: {$audioUrl}</p>";
                }
                if ($localImageUrl) {
                    echo "<p>✅ Image downloaded locally: {$localImageUrl}</p>";
                }
                if ($lyrics) {
                    echo "<p>✅ Lyrics extracted (" . strlen($lyrics) . " chars)</p>";
                }
                
                // Process variations from API response
                if (isset($api_data['data']['data']) && is_array($api_data['data']['data']) && count($api_data['data']['data']) > 1) {
                    echo "<p>đŸŽĩ Processing variations from API response...</p>";
                    
                    // Get the track ID
                    $trackIdForVariations = $trackId;
                    
                    // Clear existing variations
                    $clearStmt = $pdo->prepare("DELETE FROM audio_variations WHERE track_id = ?");
                    $clearStmt->execute([$trackIdForVariations]);
                    
                    $storedVariationsCount = 0;
                    $variationIndex = 0;
                    $audioData = $api_data['data']['data'];
                    
                    foreach ($audioData as $originalIndex => $variation) {
                        // Check for audio URL
                        $variationAudioUrl = (!empty($variation['audio_url'])) ? $variation['audio_url'] : 
                                            ((!empty($variation['source_audio_url'])) ? $variation['source_audio_url'] : 
                                            ((!empty($variation['stream_audio_url'])) ? $variation['stream_audio_url'] : null));
                        
                        if (!empty($variationAudioUrl)) {
                            // Download variation audio
                            $localVariationUrl = downloadAndStoreAudio($variationAudioUrl, $taskId, 'variation', $variationIndex);
                            
                            // Extract variation title
                            $variationTitle = $variation['title'] ?? null;
                            if (!$variationTitle) {
                                $titleParts = [];
                                if (isset($variation['genre'])) $titleParts[] = $variation['genre'];
                                if (isset($variation['style'])) $titleParts[] = $variation['style'];
                                if (isset($variation['mood'])) $titleParts[] = $variation['mood'];
                                if (isset($variation['energy'])) $titleParts[] = $variation['energy'];
                                
                                if (!empty($titleParts)) {
                                    $variationTitle = implode(' ', $titleParts) . ' Variation';
                                } else {
                                    $variationTitle = "AI Variation " . ($variationIndex + 1);
                                }
                            }
                            
                            // Build variation metadata
                            $variationMetadata = [
                                'genre' => $variation['genre'] ?? (is_array($variation['tags']) ? $variation['tags'][0] : (is_string($variation['tags']) ? explode(',', $variation['tags'])[0] : null)),
                                'style' => $variation['style'] ?? null,
                                'bpm' => $variation['bpm'] ?? $variation['tempo'] ?? null,
                                'key' => $variation['key'] ?? null,
                                'mood' => $variation['mood'] ?? null,
                                'energy' => $variation['energy'] ?? null,
                                'instruments' => $variation['instruments'] ?? null,
                                'tags' => $variation['tags'] ?? null,
                                'duration' => $variation['duration'] ?? null,
                                'title' => $variationTitle
                            ];
                            
                            // Download variation image if available
                            $variationImageUrl = null;
                            if (isset($variation['image_url']) && !empty($variation['image_url'])) {
                                $variationImageUrl = downloadAndStoreImage($variation['image_url'], $taskId . '_var' . $variationIndex);
                            } elseif (isset($variation['source_image_url']) && !empty($variation['source_image_url'])) {
                                $variationImageUrl = downloadAndStoreImage($variation['source_image_url'], $taskId . '_var' . $variationIndex);
                            }
                            
                            // Insert variation
                            try {
                                $variationStmt = $pdo->prepare("
                                    INSERT INTO audio_variations 
                                    (track_id, variation_index, audio_url, duration, title, tags, image_url, source_audio_url, stream_audio_url, metadata) 
                                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                                ");
                                
                                $variationTags = is_array($variation['tags']) ? implode(', ', $variation['tags']) : $variation['tags'];
                                
                                $insertResult = $variationStmt->execute([
                                    $trackIdForVariations,
                                    $variationIndex,
                                    $localVariationUrl ?: $variationAudioUrl,
                                    $variation['duration'] ?? null,
                                    $variationTitle,
                                    $variationTags,
                                    $variationImageUrl,
                                    $variation['source_audio_url'] ?? null,
                                    $variation['stream_audio_url'] ?? null,
                                    json_encode($variationMetadata)
                                ]);
                                
                                if ($insertResult) {
                                    $storedVariationsCount++;
                                    $variationIndex++;
                                    echo "<p style='color: green;'>✅ Stored variation {$variationIndex}: {$variationTitle}</p>";
                                } else {
                                    $errorInfo = $variationStmt->errorInfo();
                                    echo "<p style='color: red;'>❌ Failed to insert variation: " . htmlspecialchars(json_encode($errorInfo)) . "</p>";
                                }
                            } catch (Exception $e) {
                                echo "<p style='color: red;'>❌ Exception inserting variation: " . htmlspecialchars($e->getMessage()) . "</p>";
                            }
                        } else {
                            echo "<p style='color: orange;'>âš ī¸ Skipping variation {$originalIndex} - no audio URL</p>";
                        }
                    }
                    
                        // Update variations_count on main track - CRITICAL: This is what makes the variation button show!
                        if ($storedVariationsCount > 0) {
                            $updateVarCountStmt = $pdo->prepare("UPDATE music_tracks SET variations_count = ?, selected_variation = 0 WHERE id = ?");
                            $updateVarCountStmt->execute([$storedVariationsCount, $trackIdForVariations]);
                            
                            // Verify the update worked
                            $verifyCountStmt = $pdo->prepare("SELECT variations_count FROM music_tracks WHERE id = ?");
                            $verifyCountStmt->execute([$trackIdForVariations]);
                            $updatedCount = $verifyCountStmt->fetchColumn();
                            
                            // Double-check: count actual variations in database
                            $actualCountStmt = $pdo->prepare("SELECT COUNT(*) FROM audio_variations WHERE track_id = ?");
                            $actualCountStmt->execute([$trackIdForVariations]);
                            $actualCount = $actualCountStmt->fetchColumn();
                            
                            echo "<p style='color: green; font-weight: bold;'>✅ Stored {$storedVariationsCount} variation(s)</p>";
                            echo "<p>📊 <strong>variations_count field:</strong> {$updatedCount}</p>";
                            echo "<p>📊 <strong>Actual variations in DB:</strong> {$actualCount}</p>";
                            
                            if ($actualCount == $storedVariationsCount && $updatedCount == $storedVariationsCount) {
                                echo "<p style='color: green;'>✅ Variation button should now show! (variations_count = {$updatedCount})</p>";
                            } else {
                                echo "<p style='color: red;'>âš ī¸ MISMATCH: Expected {$storedVariationsCount}, DB has {$actualCount}, field shows {$updatedCount}</p>";
                                
                                // Fix the count if wrong
                                if ($actualCount != $updatedCount) {
                                    $fixCountStmt = $pdo->prepare("UPDATE music_tracks SET variations_count = ? WHERE id = ?");
                                    $fixCountStmt->execute([$actualCount, $trackIdForVariations]);
                                    echo "<p style='color: green;'>✅ Fixed variations_count to match actual count: {$actualCount}</p>";
                                }
                            }
                        } else {
                            echo "<p style='color: orange;'>âš ī¸ No variations stored (all items may have had empty audio URLs)</p>";
                        }
                }
                
                $fixedCount++;
                
                $logEntry = "✅ Successfully fixed track #{$trackId}\n";
                file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
            } else {
            echo "<p style='color: red;'>❌ Failed to update track in database</p>";
            $errorCount++;
        }
        
    } elseif ($api_status === 'processing') {
        echo "<p style='color: orange;'>âŗ Still processing</p>";
        $stillProcessingCount++;
    } elseif ($api_status === 'failed' || $api_status === 'error' || $api_status === 'rejected') {
        echo "<p style='color: red;'>❌ Track failed</p>";
        
        // Sanitize error message - remove API.Box references only
        $api_error_msg = $api_data['error'] ?? $api_data['msg'] ?? $api_data['error_msg'] ?? 'Generation failed';
        $sanitized_error = preg_replace('/\b(API\.Box|api\.box|API\.box|not found on API\.Box|not found in API\.Box|Task not found in API\.Box|Track.*not found on API\.Box)\b/i', '', $api_error_msg);
        $sanitized_error = trim($sanitized_error);
        $sanitized_error = preg_replace('/\s+/', ' ', $sanitized_error);
        if (empty($sanitized_error)) {
            $sanitized_error = 'Generation failed';
        }
        
        // Mark as failed
        $error_metadata = json_encode([
            'code' => $api_data['code'] ?? 531,
            'msg' => $sanitized_error,
            'error_type' => 'generation_failed',
            'data' => $api_data,
            'timestamp' => date('Y-m-d H:i:s'),
            'fixed_by' => 'fix_stuck_processing_tracks'
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        
        $update_stmt = $pdo->prepare("UPDATE music_tracks SET status = 'failed', metadata = ?, updated_at = NOW() WHERE id = ?");
        if ($update_stmt->execute([$error_metadata, $trackId])) {
            echo "<p style='color: green;'>✅ Updated track to 'failed' status</p>";
            
            // Check if credit was already refunded
            $check_stmt = $pdo->prepare("
                SELECT COUNT(*) 
                FROM credit_transactions 
                WHERE user_id = ? 
                AND type = 'refund' 
                AND (description LIKE ? OR description LIKE ? OR description LIKE ?)
            ");
            $check_stmt->execute([
                $track['user_id'],
                "%Track failed: {$trackId}%",
                "%Track #{$trackId}%",
                "%track {$trackId}%"
            ]);
            $already_refunded = $check_stmt->fetchColumn() > 0;
            
            if (!$already_refunded) {
                // Refund credit
                $refund_stmt = $pdo->prepare("UPDATE users SET credits = credits + 1 WHERE id = ?");
                if ($refund_stmt->execute([$track['user_id']])) {
                    // Record refund transaction
                    $trans_stmt = $pdo->prepare("
                        INSERT INTO credit_transactions (user_id, amount, type, description, created_at) 
                        VALUES (?, 1, 'refund', 'Track failed (stuck fix): {$trackId} - {$track['title']}', NOW())
                    ");
                    $trans_stmt->execute([$track['user_id']]);
                    echo "<p style='color: green;'>✅ Refunded 1 credit</p>";
                }
            } else {
                echo "<p style='color: blue;'>â„šī¸ Credit already refunded</p>";
            }
            
            $failedCount++;
        }
    } else {
        echo "<p style='color: orange;'>âš ī¸ Unknown API status: " . htmlspecialchars($api_status ?? 'N/A') . "</p>";
        echo "<pre>" . htmlspecialchars(json_encode($api_data, JSON_PRETTY_PRINT)) . "</pre>";
        $errorCount++;
    }
    
    echo "</div>";
}

echo "<hr>";
echo "<h2>📊 Summary</h2>";
echo "<p><strong>Fixed (processing → complete):</strong> {$fixedCount}</p>";
echo "<p><strong>Still Processing:</strong> {$stillProcessingCount}</p>";
echo "<p><strong>Marked as Failed:</strong> {$failedCount}</p>";
echo "<p><strong>Errors:</strong> {$errorCount}</p>";
echo "<p><strong>Total Checked:</strong> " . count($stuckTracks) . "</p>";

if ($fixedCount > 0) {
    echo "<p style='color: green; font-size: 18px; font-weight: bold;'>✅ Successfully fixed {$fixedCount} track(s)!</p>";
}

echo "<p><a href='admin.php?tab=track-status'>← Back to Admin</a></p>";


CasperSecurity Mini