![]() 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/api/ |
<?php
require_once '../config/database.php';
require_once '../utils/audio_token.php';
session_start();
header('Content-Type: application/json');
$track_id = $_GET['id'] ?? null;
$user_id = $_SESSION['user_id'] ?? null;
if (!$track_id) {
echo json_encode(['success' => false, 'error' => 'Track ID required']);
exit;
}
try {
$pdo = getDBConnection();
// Check if track_votes table exists
$votes_table_exists = false;
try {
$check_stmt = $pdo->query("SHOW TABLES LIKE 'track_votes'");
$votes_table_exists = $check_stmt->rowCount() > 0;
} catch (Exception $e) {
$votes_table_exists = false;
}
// Get the specific track with all details
if ($votes_table_exists) {
$track_query = "
SELECT
mt.*,
u.name as artist_name,
u.plan as artist_plan,
COALESCE(tp.play_count, 0) as play_count,
COALESCE(tl.like_count, 0) as like_count,
COALESCE(tv.vote_count, 0) as vote_count,
CASE WHEN tl_user.track_id IS NOT NULL THEN 1 ELSE 0 END as user_liked,
tv_user.vote_type as user_vote
FROM music_tracks mt
JOIN users u ON mt.user_id = u.id
LEFT JOIN (SELECT track_id, COUNT(*) as play_count FROM track_plays GROUP BY track_id) tp ON mt.id = tp.track_id
LEFT JOIN (SELECT track_id, COUNT(*) as like_count FROM track_likes GROUP BY track_id) tl ON mt.id = tl.track_id
LEFT JOIN (
SELECT
track_id,
SUM(CASE WHEN vote_type = 'up' THEN 1 ELSE -1 END) as vote_count
FROM track_votes
GROUP BY track_id
) tv ON mt.id = tv.track_id
LEFT JOIN track_likes tl_user ON mt.id = tl_user.track_id AND tl_user.user_id = ?
LEFT JOIN track_votes tv_user ON mt.id = tv_user.track_id AND tv_user.user_id = ?
WHERE mt.id = ? AND mt.status = 'complete'
";
$stmt = $pdo->prepare($track_query);
$stmt->execute([$user_id, $user_id, $track_id]);
} else {
$track_query = "
SELECT
mt.*,
u.name as artist_name,
u.plan as artist_plan,
COALESCE(tp.play_count, 0) as play_count,
COALESCE(tl.like_count, 0) as like_count,
0 as vote_count,
CASE WHEN tl_user.track_id IS NOT NULL THEN 1 ELSE 0 END as user_liked,
NULL as user_vote
FROM music_tracks mt
JOIN users u ON mt.user_id = u.id
LEFT JOIN (SELECT track_id, COUNT(*) as play_count FROM track_plays GROUP BY track_id) tp ON mt.id = tp.track_id
LEFT JOIN (SELECT track_id, COUNT(*) as like_count FROM track_likes GROUP BY track_id) tl ON mt.id = tl.track_id
LEFT JOIN track_likes tl_user ON mt.id = tl_user.track_id AND tl_user.user_id = ?
WHERE mt.id = ? AND mt.status = 'complete'
";
$stmt = $pdo->prepare($track_query);
$stmt->execute([$user_id, $track_id]);
}
$track = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$track) {
echo json_encode(['success' => false, 'error' => 'Track not found']);
exit;
}
// SECURITY: Check if track is private and user has access
// Only the track owner can view private tracks, OR users with a valid share token
$isOwner = ($user_id && $track['user_id'] == $user_id);
$isPublic = ($track['is_public'] == 1 || $track['is_public'] === null);
// Check for share token
$share_token = $_GET['share'] ?? '';
$hasValidShareToken = false;
if (!empty($share_token) && !$isPublic) {
require_once __DIR__ . '/../utils/share_token.php';
$hasValidShareToken = isValidShareToken($track_id, $share_token);
}
// If track is private, check access
if (!$isPublic) {
if (!$isOwner && !$hasValidShareToken) {
// User is not the owner and doesn't have a valid share token - deny access
echo json_encode(['success' => false, 'error' => 'Access denied. This track is private.']);
exit;
}
}
// Check if this is a variation and get the main track title
$display_title = $track['title'];
if (!empty($track['task_id'])) {
$main_track_query = "
SELECT id, title
FROM music_tracks
WHERE task_id = ? AND status = 'complete'
ORDER BY created_at ASC, id ASC
LIMIT 1
";
$main_track_stmt = $pdo->prepare($main_track_query);
$main_track_stmt->execute([$track['task_id']]);
$main_track = $main_track_stmt->fetch(PDO::FETCH_ASSOC);
// If main track exists and is different from current track, use main track's title
if ($main_track && $main_track['id'] != $track['id'] && !empty($main_track['title'])) {
$display_title = $main_track['title'];
}
}
// Get all variations for this track (CRITICAL FIX: fetch ALL variations, not just one)
$trackVariations = [];
if ($track['variations_count'] > 0) {
try {
$var_stmt = $pdo->prepare("
SELECT
variation_index,
audio_url,
duration,
title,
tags,
image_url,
source_audio_url
FROM audio_variations
WHERE track_id = ?
ORDER BY variation_index ASC
");
$var_stmt->execute([$track_id]);
$trackVariations = $var_stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
error_log("Error getting variations for track {$track_id} in get_track_data.php: " . $e->getMessage());
$trackVariations = [];
}
}
// CRITICAL SECURITY: Sanitize audio URLs before returning
// Replace raw audio_url with signed URL that requires token validation
$signedAudioUrl = getSignedAudioUrl($track['id']);
// Sanitize variations - remove raw audio URLs
$sanitizedVariations = [];
foreach ($trackVariations as $var) {
$sanitizedVar = $var;
// Replace raw audio_url with signed URL
if (isset($var['variation_index'])) {
$sanitizedVar['audio_url'] = getSignedAudioUrl($track['id'], $var['variation_index']);
} else {
$sanitizedVar['audio_url'] = $signedAudioUrl; // Fallback to main track signed URL
}
// Remove source URLs
unset($sanitizedVar['source_audio_url']);
$sanitizedVariations[] = $sanitizedVar;
}
// Parse metadata for BPM/key
$metadata = json_decode($track['metadata'] ?? '{}', true) ?: [];
// Return track data with all variations
echo json_encode([
'success' => true,
'track' => [
'id' => $track['id'],
'title' => $display_title,
'artist_name' => $track['artist_name'],
'user_id' => $track['user_id'],
'prompt' => $track['prompt'],
'audio_url' => $signedAudioUrl, // Use signed URL instead of raw path
'signed_audio_url' => $signedAudioUrl, // Explicit signed URL field
'image_url' => $track['image_url'],
'duration' => $track['duration'],
'play_count' => $track['play_count'],
'like_count' => $track['like_count'],
'vote_count' => $track['vote_count'] ?? 0,
'user_liked' => $track['user_liked'],
'user_vote' => $track['user_vote'] ?? null,
'variations_count' => $track['variations_count'] ?? 0,
'variations' => $sanitizedVariations, // Sanitized variations
'metadata' => $metadata // Include metadata for BPM/key updates
]
]);
} catch (Exception $e) {
error_log("Track data API error: " . $e->getMessage());
echo json_encode(['success' => false, 'error' => 'Internal server error']);
}
?>