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

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/public_html/admin_batch_analyze_tracks.php
<?php
/**
 * Batch Audio Analysis - Admin Tool
 * Analyzes all tracks without analyzed metadata using client-side Web Audio API
 */

require_once 'config/database.php';
require_once 'utils/audio_token.php';
session_start();

// Check admin access
if (!isset($_SESSION['user_id'])) {
    header('Location: /auth/login.php');
    exit;
}

// Check if user is admin (you may need to adjust this check)
$pdo = getDBConnection();
$stmt = $pdo->prepare("SELECT plan FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

// For now, allow any logged-in user (you can restrict to admins only)
// if ($user['plan'] !== 'admin') {
//     die('Access denied');
// }

// Get batch size from query param (default 50 to avoid browser timeout)
$batchSize = isset($_GET['batch_size']) ? intval($_GET['batch_size']) : 50;
$offset = isset($_GET['offset']) ? intval($_GET['offset']) : 0;

// Get tracks that need analysis
$stmt = $pdo->prepare("
    SELECT 
        mt.id,
        mt.title,
        mt.audio_url,
        mt.metadata,
        u.name as artist_name
    FROM music_tracks mt
    JOIN users u ON mt.user_id = u.id
    WHERE mt.status = 'complete'
    AND mt.audio_url IS NOT NULL
    AND mt.audio_url != ''
    AND (
        JSON_EXTRACT(mt.metadata, '$.analyzed_bpm') IS NULL
        OR JSON_EXTRACT(mt.metadata, '$.analysis_source') IS NULL
    )
    ORDER BY mt.created_at DESC
    LIMIT ? OFFSET ?
");

$stmt->execute([$batchSize, $offset]);
$tracksRaw = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Get total count for display
$countStmt = $pdo->prepare("
    SELECT COUNT(*) as total
    FROM music_tracks mt
    WHERE mt.status = 'complete'
    AND mt.audio_url IS NOT NULL
    AND mt.audio_url != ''
    AND (
        JSON_EXTRACT(mt.metadata, '$.analyzed_bpm') IS NULL
        OR JSON_EXTRACT(mt.metadata, '$.analysis_source') IS NULL
    )
");
$countStmt->execute();
$totalCount = $countStmt->fetchColumn();

// Generate signed URLs for each track
$tracksToAnalyze = [];
foreach ($tracksRaw as $track) {
    $signedUrl = getSignedAudioUrl($track['id'], null, 3600, $_SESSION['user_id'], session_id());
    $tracksToAnalyze[] = [
        'id' => $track['id'],
        'title' => $track['title'],
        'audio_url' => $signedUrl,
        'artist_name' => $track['artist_name']
    ];
}

$totalTracks = count($tracksToAnalyze);

?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Batch Audio Analysis - SoundStudioPro</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
            background: #1a1a1a;
            color: #fff;
            padding: 2rem;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        h1 {
            margin-bottom: 2rem;
            color: #667eea;
        }
        
        .stats {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 1rem;
            margin-bottom: 2rem;
        }
        
        .stat-card {
            background: rgba(255, 255, 255, 0.05);
            padding: 1.5rem;
            border-radius: 12px;
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        
        .stat-label {
            font-size: 0.9rem;
            color: #888;
            margin-bottom: 0.5rem;
        }
        
        .stat-value {
            font-size: 2rem;
            font-weight: 700;
            color: #667eea;
        }
        
        .controls {
            display: flex;
            gap: 1rem;
            margin-bottom: 2rem;
        }
        
        .btn {
            padding: 12px 24px;
            border: none;
            border-radius: 8px;
            font-size: 1rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s;
        }
        
        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        
        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
        }
        
        .btn-secondary {
            background: rgba(255, 255, 255, 0.1);
            color: white;
        }
        
        .btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }
        
        .progress-bar {
            width: 100%;
            height: 30px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 15px;
            overflow: hidden;
            margin-bottom: 1rem;
        }
        
        .progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            transition: width 0.3s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: 600;
            font-size: 0.9rem;
        }
        
        .log {
            background: rgba(0, 0, 0, 0.3);
            border-radius: 8px;
            padding: 1rem;
            max-height: 400px;
            overflow-y: auto;
            font-family: 'Courier New', monospace;
            font-size: 0.85rem;
            margin-top: 2rem;
        }
        
        .log-entry {
            padding: 0.25rem 0;
            border-bottom: 1px solid rgba(255, 255, 255, 0.05);
        }
        
        .log-entry.success {
            color: #48bb78;
        }
        
        .log-entry.error {
            color: #ef4444;
        }
        
        .log-entry.info {
            color: #667eea;
        }
        
        .current-track {
            background: rgba(102, 126, 234, 0.1);
            padding: 1rem;
            border-radius: 8px;
            margin-bottom: 1rem;
            border-left: 4px solid #667eea;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1><i class="fas fa-wave-square"></i> Batch Audio Analysis</h1>
        <p style="color: #888; margin-bottom: 2rem;">
            This tool analyzes tracks using your browser's Web Audio API. 
            Processing <?= $totalTracks ?> tracks in this batch (<?= $totalCount ?> total needing analysis).
            <?php if ($offset > 0): ?>
                <br><a href="?offset=<?= max(0, $offset - $batchSize) ?>&batch_size=<?= $batchSize ?>" style="color: #667eea;">← Previous Batch</a>
            <?php endif; ?>
            <?php if ($offset + $batchSize < $totalCount): ?>
                <a href="?offset=<?= $offset + $batchSize ?>&batch_size=<?= $batchSize ?>" style="color: #667eea; margin-left: 1rem;">Next Batch →</a>
            <?php endif; ?>
        </p>
        
        <div class="stats">
            <div class="stat-card">
                <div class="stat-label">Total Needing Analysis</div>
                <div class="stat-value" id="totalTracks"><?= $totalCount ?></div>
            </div>
            <div class="stat-card">
                <div class="stat-label">In This Batch</div>
                <div class="stat-value" id="batchSize"><?= $totalTracks ?></div>
            </div>
            <div class="stat-card">
                <div class="stat-label">Analyzed</div>
                <div class="stat-value" id="analyzedCount">0</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">Failed</div>
                <div class="stat-value" id="failedCount">0</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">Progress</div>
                <div class="stat-value" id="progressPercent">0%</div>
            </div>
        </div>
        
        <div class="controls">
            <button class="btn btn-primary" id="startBtn" onclick="startAnalysis()">
                <i class="fas fa-play"></i> Start Analysis
            </button>
            <button class="btn btn-secondary" id="pauseBtn" onclick="pauseAnalysis()" disabled>
                <i class="fas fa-pause"></i> Pause
            </button>
            <button class="btn btn-secondary" id="stopBtn" onclick="stopAnalysis()" disabled>
                <i class="fas fa-stop"></i> Stop
            </button>
        </div>
        
        <div class="progress-bar">
            <div class="progress-fill" id="progressFill" style="width: 0%">0%</div>
        </div>
        
        <div class="current-track" id="currentTrack" style="display: none;">
            <strong>Analyzing:</strong> <span id="currentTrackName">-</span>
        </div>
        
        <div class="log" id="log"></div>
    </div>
    
    <script src="/js/audio_analyzer_simple.js?v=<?= time() ?>"></script>
    <script>
        const tracks = <?= json_encode($tracksToAnalyze) ?>;
        let currentIndex = 0;
        let isRunning = false;
        let isPaused = false;
        let analyzedCount = 0;
        let failedCount = 0;
        
        // Ensure simple analyzer is loaded
        if (!window.simpleAudioAnalyzer) {
            log('⚠️ Waiting for analyzer to load...', 'info');
            const checkInterval = setInterval(() => {
                if (window.simpleAudioAnalyzer) {
                    clearInterval(checkInterval);
                    log('✅ Analyzer loaded', 'success');
                }
            }, 100);
        }
        
        function log(message, type = 'info') {
            const logDiv = document.getElementById('log');
            const entry = document.createElement('div');
            entry.className = `log-entry ${type}`;
            entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
            logDiv.appendChild(entry);
            logDiv.scrollTop = logDiv.scrollHeight;
        }
        
        function updateStats() {
            const total = tracks.length;
            const progress = total > 0 ? Math.round((analyzedCount + failedCount) / total * 100) : 0;
            
            document.getElementById('analyzedCount').textContent = analyzedCount;
            document.getElementById('failedCount').textContent = failedCount;
            document.getElementById('progressPercent').textContent = progress + '%';
            document.getElementById('progressFill').style.width = progress + '%';
            document.getElementById('progressFill').textContent = progress + '%';
        }
        
        async function analyzeNextTrack() {
            if (currentIndex >= tracks.length) {
                stopAnalysis();
                log('✅ All tracks analyzed!', 'success');
                return;
            }
            
            if (isPaused) {
                return;
            }
            
            const track = tracks[currentIndex];
            currentIndex++;
            
            // Show current track
            document.getElementById('currentTrack').style.display = 'block';
            document.getElementById('currentTrackName').textContent = 
                `${track.title} by ${track.artist_name} (Track #${track.id})`;
            
            log(`Analyzing track ${currentIndex}/${tracks.length}: ${track.title}`, 'info');
            
            try {
                // Get signed audio URL via API (more secure)
                const tokenResponse = await fetch(`/api/get_audio_token.php?track_id=${track.id}`);
                if (!tokenResponse.ok) {
                    throw new Error('Failed to get audio token');
                }
                
                const tokenData = await tokenResponse.json();
                if (!tokenData.success || !tokenData.url) {
                    throw new Error(tokenData.error || 'Invalid audio URL');
                }
                
                let audioUrl = tokenData.url;
                if (!audioUrl.startsWith('http')) {
                    audioUrl = window.location.origin + audioUrl;
                }
                
                // Ensure simple analyzer is available
                if (!window.simpleAudioAnalyzer) {
                    throw new Error('Audio analyzer not loaded');
                }
                
                // Analyze with improved simple analyzer (better BPM/key detection)
                const result = await window.simpleAudioAnalyzer.analyzeAudio(audioUrl);
                
                if (result) {
                    // Save to server
                    const saveResponse = await fetch('/api/save_audio_analysis.php', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({
                            track_id: track.id,
                            bpm: result.bpm,
                            key: result.key,
                            camelot: result.camelot,
                            energy: result.energy,
                            confidence: result.confidence || 80
                        })
                    });
                    
                    const saveData = await saveResponse.json();
                    
                    if (saveData.success) {
                        analyzedCount++;
                        // Log what was actually saved (from server response)
                        const savedBpm = saveData.data?.bpm ?? result.bpm;
                        const savedKey = saveData.data?.key ?? result.key;
                        const savedCamelot = saveData.data?.camelot ?? result.camelot;
                        log(`✅ ${track.title}: BPM=${savedBpm}, Key=${savedKey} (${savedCamelot}), Energy=${result.energy}`, 'success');
                        
                        // Notify track.php page if it's open to update the display
                        window.postMessage({
                            type: 'analysis_complete',
                            trackId: track.id,
                            bpm: savedBpm,
                            key: savedKey,
                            camelot: savedCamelot
                        }, '*');
                    } else {
                        failedCount++;
                        log(`❌ Failed to save: ${saveData.error || 'Unknown error'}`, 'error');
                        console.error('Save error details:', saveData);
                    }
                } else {
                    failedCount++;
                    log(`❌ Analysis returned no result`, 'error');
                }
                
            } catch (error) {
                failedCount++;
                log(`❌ Error analyzing ${track.title}: ${error.message}`, 'error');
            }
            
            updateStats();
            
            // Continue to next track (with small delay to prevent browser freezing)
            if (isRunning) {
                setTimeout(analyzeNextTrack, 500);
            }
        }
        
        function startAnalysis() {
            if (isRunning) return;
            
            isRunning = true;
            isPaused = false;
            document.getElementById('startBtn').disabled = true;
            document.getElementById('pauseBtn').disabled = false;
            document.getElementById('stopBtn').disabled = false;
            
            log('🚀 Starting batch analysis...', 'info');
            analyzeNextTrack();
        }
        
        function pauseAnalysis() {
            isPaused = !isPaused;
            document.getElementById('pauseBtn').textContent = isPaused ? 
                '<i class="fas fa-play"></i> Resume' : 
                '<i class="fas fa-pause"></i> Pause';
            
            if (!isPaused) {
                analyzeNextTrack();
            }
        }
        
        function stopAnalysis() {
            isRunning = false;
            isPaused = false;
            document.getElementById('startBtn').disabled = false;
            document.getElementById('pauseBtn').disabled = true;
            document.getElementById('stopBtn').disabled = true;
            document.getElementById('currentTrack').style.display = 'none';
            
            log('⏹️ Analysis stopped', 'info');
        }
        
        // Initialize
        updateStats();
        log(`Ready to analyze ${tracks.length} tracks`, 'info');
    </script>
</body>
</html>


CasperSecurity Mini