![]() 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
header('Content-Type: application/json');
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../utils/audio_token.php';
// Start session to get current user context
session_start();
// Get parameters
$page = max(1, intval($_GET['page'] ?? 1));
$per_page = intval($_GET['per_page'] ?? 24);
$offset = ($page - 1) * $per_page;
$sort = $_GET['sort'] ?? 'latest';
$genre = $_GET['genre'] ?? 'all';
if ($genre !== 'all') {
$genre = urldecode($genre);
$genre = trim($genre, '"\'');
$genre = trim($genre);
// Validate genre - reject JSON objects or invalid formats
// Check if it looks like JSON (starts with { or [)
if (preg_match('/^[\s]*[{\[]/', $genre)) {
// This is JSON, not a valid genre - reset to 'all'
error_log("API: Invalid genre parameter detected (JSON): " . substr($genre, 0, 100));
$genre = 'all';
}
// Check if it contains JSON-like structures (callBackUrl, etc.)
if (stripos($genre, 'callBackUrl') !== false || stripos($genre, 'callback') !== false) {
// This contains callback URL data, not a valid genre - reset to 'all'
error_log("API: Invalid genre parameter detected (contains callback): " . substr($genre, 0, 100));
$genre = 'all';
}
// Additional validation: genre should be a simple string, not contain JSON characters
if (preg_match('/[{}[\]]/', $genre)) {
// Contains JSON brackets - invalid genre
error_log("API: Invalid genre parameter detected (contains JSON brackets): " . substr($genre, 0, 100));
$genre = 'all';
}
}
$time_filter = $_GET['time'] ?? 'all';
$search = $_GET['search'] ?? '';
if (!empty($search)) {
$search = urldecode($search);
$search = trim($search, '"\'');
$search = trim($search);
}
$user_id = $_SESSION['user_id'] ?? null;
try {
$pdo = getDBConnection();
// Build time filter
$time_condition = '';
switch ($time_filter) {
case 'today':
$time_condition = 'AND mt.created_at >= CURDATE()';
break;
case 'week':
$time_condition = 'AND mt.created_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)';
break;
case 'month':
$time_condition = 'AND mt.created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)';
break;
}
// Build sort order - use subqueries to match SELECT structure
$order_by = 'mt.created_at DESC';
switch ($sort) {
case 'trending':
// OPTIMIZED: Use JOIN aliases instead of subqueries
$order_by = '(COALESCE(like_stats.like_count, 0) * 2 + COALESCE(play_stats.play_count, 0) + COALESCE(comment_stats.comment_count, 0)) DESC, mt.created_at DESC';
break;
case 'popular':
// OPTIMIZED: Use JOIN aliases instead of subqueries
$order_by = 'COALESCE(like_stats.like_count, 0) DESC, COALESCE(play_stats.play_count, 0) DESC';
break;
case 'random':
$order_by = 'RAND()';
break;
default:
$order_by = 'mt.created_at DESC';
}
// Build genre filter
$genre_condition = '';
if ($genre !== 'all' && !empty($genre)) {
$genre_lower = strtolower(trim($genre));
$genre_like = '%' . $genre_lower . '%';
$genre_condition = "AND (
LOWER(mt.genre) LIKE ?
OR LOWER(mt.tags) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.genre'))) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.style'))) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.genre') AS CHAR)) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.style') AS CHAR)) LIKE ?
)";
}
// Build search filter
$search_condition = '';
if (!empty($search)) {
$search_lower = strtolower(trim($search));
$search_param = '%' . $search_lower . '%';
$search_condition = "AND (
LOWER(mt.title) LIKE ?
OR LOWER(u.name) LIKE ?
OR LOWER(COALESCE(mt.prompt, '')) LIKE ?
OR LOWER(COALESCE(mt.lyrics, '')) LIKE ?
OR LOWER(mt.genre) LIKE ?
OR LOWER(mt.tags) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.genre'))) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.style'))) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.genre') AS CHAR)) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.style') AS CHAR)) LIKE ?
OR LOWER(mt.style) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.proGenre') AS CHAR)) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.proGenre'))) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.proSubGenre') AS CHAR)) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.proSubGenre'))) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.tags') AS CHAR)) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.tags'))) LIKE ?
OR LOWER(COALESCE(mt.mood, '')) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.mood') AS CHAR)) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.mood'))) LIKE ?
OR LOWER(COALESCE(mt.instruments, '')) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.instruments') AS CHAR)) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.instruments'))) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.proLeadInstrument') AS CHAR)) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.proLeadInstrument'))) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.proRhythmSection') AS CHAR)) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.proRhythmSection'))) LIKE ?
OR LOWER(COALESCE(mt.energy, '')) LIKE ?
OR LOWER(CAST(JSON_EXTRACT(mt.metadata, '$.energy') AS CHAR)) LIKE ?
OR LOWER(JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.energy'))) LIKE ?
)";
}
// Get total count
$count_sql = "
SELECT COUNT(*) as total
FROM music_tracks mt
INNER JOIN users u ON mt.user_id = u.id
WHERE mt.status = 'complete'
AND (mt.audio_url IS NOT NULL AND mt.audio_url != '' OR mt.variations_count > 0)
AND mt.user_id IS NOT NULL
AND u.id IS NOT NULL
AND (mt.is_public = 1 OR mt.is_public IS NULL)
$time_condition
$genre_condition
$search_condition
";
$count_stmt = $pdo->prepare($count_sql);
$count_params = [];
if ($genre !== 'all' && !empty($genre)) {
$genre_like = '%' . strtolower(trim($genre)) . '%';
$count_params = array_merge($count_params, [$genre_like, $genre_like, $genre_like, $genre_like, $genre_like, $genre_like]);
}
if (!empty($search)) {
$search_param = '%' . strtolower(trim($search)) . '%';
$count_params = array_merge($count_params, array_fill(0, 30, $search_param));
}
$count_stmt->execute($count_params);
$total_tracks = $count_stmt->fetchColumn();
// OPTIMIZED: Using JOINs instead of correlated subqueries for better performance
// Get tracks
$sql = "
SELECT
mt.id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.user_id,
mt.genre,
mt.tags,
mt.image_url,
mt.task_id,
mt.metadata,
mt.variations_count,
mt.price,
u.name as artist_name,
u.id as artist_id,
u.profile_image,
COALESCE(like_stats.like_count, 0) as like_count,
COALESCE(comment_stats.comment_count, 0) as comment_count,
COALESCE(play_stats.play_count, 0) as play_count,
COALESCE(share_stats.share_count, 0) as share_count,
CASE WHEN user_like_stats.track_id IS NOT NULL THEN 1 ELSE 0 END as user_liked,
COALESCE((SELECT COUNT(*) FROM user_follows WHERE follower_id = ? AND following_id = mt.user_id), 0) as is_following,
CASE WHEN wishlist_stats.track_id IS NOT NULL THEN 1 ELSE 0 END as is_in_wishlist
FROM music_tracks mt
INNER JOIN users u ON mt.user_id = u.id
LEFT JOIN (SELECT track_id, COUNT(*) as like_count FROM track_likes GROUP BY track_id) like_stats ON mt.id = like_stats.track_id
LEFT JOIN (SELECT track_id, COUNT(*) as comment_count FROM track_comments GROUP BY track_id) comment_stats ON mt.id = comment_stats.track_id
LEFT JOIN (SELECT track_id, COUNT(*) as play_count FROM track_plays GROUP BY track_id) play_stats ON mt.id = play_stats.track_id
LEFT JOIN (SELECT track_id, COUNT(*) as share_count FROM track_shares GROUP BY track_id) share_stats ON mt.id = share_stats.track_id
LEFT JOIN (SELECT track_id FROM track_likes WHERE user_id = ?) user_like_stats ON mt.id = user_like_stats.track_id
LEFT JOIN (SELECT track_id FROM user_wishlist WHERE user_id = ?) wishlist_stats ON mt.id = wishlist_stats.track_id
WHERE mt.status = 'complete'
AND (mt.audio_url IS NOT NULL AND mt.audio_url != '' OR mt.variations_count > 0)
AND mt.user_id IS NOT NULL
AND u.id IS NOT NULL
AND (mt.is_public = 1 OR mt.is_public IS NULL)
$time_condition
$genre_condition
$search_condition
ORDER BY $order_by
LIMIT ? OFFSET ?
";
$stmt = $pdo->prepare($sql);
// Parameters: user_like_stats.user_id, wishlist_stats.user_id, is_following user_id (3 total)
$params = [$user_id ?? 0, $user_id ?? 0, $user_id ?? 0];
if ($genre !== 'all' && !empty($genre)) {
$genre_like = '%' . strtolower(trim($genre)) . '%';
$params = array_merge($params, [$genre_like, $genre_like, $genre_like, $genre_like, $genre_like, $genre_like]);
}
if (!empty($search)) {
$search_param = '%' . strtolower(trim($search)) . '%';
$params = array_merge($params, array_fill(0, 30, $search_param));
}
// Add LIMIT and OFFSET parameters
$params[] = $per_page;
$params[] = $offset;
$stmt->execute($params);
$tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get variations for each track and prepare data
foreach ($tracks as &$track) {
$track['variations'] = [];
if ($track['variations_count'] > 0) {
try {
$var_stmt = $pdo->prepare("
SELECT
variation_index,
audio_url,
duration,
title,
tags,
image_url
FROM audio_variations
WHERE track_id = ?
ORDER BY variation_index ASC
");
$var_stmt->execute([$track['id']]);
$track['variations'] = $var_stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
error_log('Error loading variations for track ' . $track['id'] . ': ' . $e->getMessage());
$track['variations'] = [];
}
}
// Resolve image URL using same logic as PHP template
$imageUrl = $track['image_url'] ?? null;
// Trim whitespace and check for empty/null values
if ($imageUrl !== null) {
$imageUrl = trim($imageUrl);
if ($imageUrl === '' || $imageUrl === 'null' || $imageUrl === 'NULL') {
$imageUrl = null;
}
}
// If image_url exists and is not empty/null, normalize and use it
if (!empty($imageUrl)) {
// Normalize local paths (add leading / if missing)
if (!str_starts_with($imageUrl, '/')) {
$imageUrl = '/' . ltrim($imageUrl, '/');
}
} else {
// Only if image_url is empty/null, try fallback sources
if (!empty($track['metadata'])) {
try {
$metadata = is_string($track['metadata']) ? json_decode($track['metadata'], true) : $track['metadata'];
if ($metadata) {
if (isset($metadata['image_url']) && !empty($metadata['image_url'])) {
$metaImageUrl = $metadata['image_url'];
// Only use if it's a local path (metadata might have external URLs)
if (strpos($metaImageUrl, 'http://') !== 0 && strpos($metaImageUrl, 'https://') !== 0) {
if (!str_starts_with($metaImageUrl, '/')) {
$metaImageUrl = '/' . ltrim($metaImageUrl, '/');
}
$imageUrl = $metaImageUrl;
}
} elseif (isset($metadata['cover_url']) && !empty($metadata['cover_url'])) {
$metaCoverUrl = $metadata['cover_url'];
// Only use if it's a local path (metadata might have external URLs)
if (strpos($metaCoverUrl, 'http://') !== 0 && strpos($metaCoverUrl, 'https://') !== 0) {
if (!str_starts_with($metaCoverUrl, '/')) {
$metaCoverUrl = '/' . ltrim($metaCoverUrl, '/');
}
$imageUrl = $metaCoverUrl;
}
}
}
} catch (Exception $e) {
error_log('Error parsing metadata for image URL: ' . $e->getMessage());
}
}
// Try to find image file by task_id pattern
if (empty($imageUrl) && !empty($track['task_id'])) {
$uploadsDir = $_SERVER['DOCUMENT_ROOT'] . '/uploads/track_covers/';
if (is_dir($uploadsDir)) {
$pattern = $uploadsDir . "track_{$track['task_id']}_*";
$files = glob($pattern);
if (!empty($files)) {
$mostRecent = end($files);
$imageUrl = '/uploads/track_covers/' . basename($mostRecent);
}
}
}
// Fallback to default only if no image found
if (empty($imageUrl)) {
$imageUrl = '/assets/images/default-track.jpg';
}
}
// Set the resolved image URL
$track['resolved_image_url'] = $imageUrl;
// Use signed audio URL
$selectedVariationIndex = null;
if (!empty($track['metadata'])) {
try {
$trackMetadata = is_string($track['metadata']) ? json_decode($track['metadata'], true) : $track['metadata'];
if ($trackMetadata && isset($trackMetadata['selected_variation'])) {
$selectedVariationIndex = (int)$trackMetadata['selected_variation'];
}
} catch (Exception $e) {
error_log('Error parsing metadata for track ' . $track['id'] . ': ' . $e->getMessage());
}
}
try {
if ($selectedVariationIndex !== null) {
$track['signed_audio_url'] = getSignedAudioUrl($track['id'], $selectedVariationIndex);
} else {
$track['signed_audio_url'] = getSignedAudioUrl($track['id']);
}
} catch (Exception $e) {
error_log('Error generating signed URL for track ' . $track['id'] . ': ' . $e->getMessage());
$track['signed_audio_url'] = '';
}
// Ensure all values are JSON-encodable
$track['id'] = (int)$track['id'];
$track['duration'] = $track['duration'] ? (int)$track['duration'] : 0;
$track['user_id'] = (int)$track['user_id'];
$track['artist_id'] = (int)$track['artist_id'];
$track['variations_count'] = (int)$track['variations_count'];
$track['price'] = $track['price'] ? (float)$track['price'] : 0;
$track['like_count'] = (int)$track['like_count'];
$track['comment_count'] = (int)$track['comment_count'];
$track['play_count'] = (int)$track['play_count'];
$track['share_count'] = (int)$track['share_count'];
$track['user_liked'] = (int)$track['user_liked'];
$track['is_following'] = (int)$track['is_following'];
$track['is_in_wishlist'] = (int)$track['is_in_wishlist'];
}
unset($track);
$response = [
'success' => true,
'tracks' => $tracks,
'pagination' => [
'page' => $page,
'per_page' => $per_page,
'total_tracks' => (int)$total_tracks,
'total_pages' => (int)ceil($total_tracks / $per_page),
'has_more' => ($page * $per_page) < $total_tracks
]
];
$json = json_encode($response);
if ($json === false) {
error_log('JSON encoding error: ' . json_last_error_msg());
throw new Exception('Failed to encode response as JSON: ' . json_last_error_msg());
}
echo $json;
} catch (PDOException $e) {
error_log('Database error in get_community_fixed_tracks.php: ' . $e->getMessage());
error_log('SQL State: ' . $e->getCode());
error_log('Stack trace: ' . $e->getTraceAsString());
http_response_code(500);
echo json_encode([
'success' => false,
'error' => 'Database error occurred while loading tracks'
]);
} catch (Exception $e) {
error_log('Error in get_community_fixed_tracks.php: ' . $e->getMessage());
error_log('Stack trace: ' . $e->getTraceAsString());
http_response_code(500);
echo json_encode([
'success' => false,
'error' => 'An error occurred while loading tracks'
]);
}
?>