![]() 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
session_start();
header('Content-Type: application/json');
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../utils/audio_token.php';
$pdo = getDBConnection();
if (!$pdo) {
echo json_encode(['success' => false, 'message' => 'Database connection failed']);
exit;
}
$user_id = $_SESSION['user_id'] ?? null;
$time_filter = $_GET['time'] ?? 'now'; // now, today, week, month
$scope_filter = $_GET['scope'] ?? 'global'; // global, following
// Build time filter condition - use published_at (when track was made public) instead of created_at
$time_condition = '';
switch ($time_filter) {
case 'now':
// "Now" means very recent - last hour
$time_condition = 'AND COALESCE(mt.published_at, mt.created_at) >= DATE_SUB(NOW(), INTERVAL 1 HOUR)';
break;
case 'today':
$time_condition = 'AND COALESCE(mt.published_at, mt.created_at) >= CURDATE()';
break;
case 'week':
$time_condition = 'AND COALESCE(mt.published_at, mt.created_at) >= DATE_SUB(NOW(), INTERVAL 1 WEEK)';
break;
case 'month':
$time_condition = 'AND COALESCE(mt.published_at, mt.created_at) >= DATE_SUB(NOW(), INTERVAL 1 MONTH)';
break;
}
// Helper function to resolve track image
function resolveTrackImage($track) {
$imageUrl = $track['image_url'] ?? null;
if ($imageUrl) {
$imageUrl = trim($imageUrl);
if ($imageUrl === '' || strtolower($imageUrl) === 'null') {
$imageUrl = null;
}
}
if (!empty($imageUrl)) {
if (!preg_match('/^https?:\/\//', $imageUrl)) {
if (!str_starts_with($imageUrl, '/')) {
$imageUrl = '/' . ltrim($imageUrl, '/');
}
return $imageUrl;
}
return $imageUrl;
}
if (!empty($track['metadata'])) {
try {
$metadata = is_string($track['metadata']) ? json_decode($track['metadata'], true) : $track['metadata'];
if ($metadata && is_array($metadata)) {
if (isset($metadata['image_url']) && !empty($metadata['image_url'])) {
$metaImageUrl = $metadata['image_url'];
if (strpos($metaImageUrl, 'http://') !== 0 && strpos($metaImageUrl, 'https://') !== 0) {
if (!str_starts_with($metaImageUrl, '/')) {
$metaImageUrl = '/' . ltrim($metaImageUrl, '/');
}
return $metaImageUrl;
}
} elseif (isset($metadata['cover_url']) && !empty($metadata['cover_url'])) {
$metaCoverUrl = $metadata['cover_url'];
if (strpos($metaCoverUrl, 'http://') !== 0 && strpos($metaCoverUrl, 'https://') !== 0) {
if (!str_starts_with($metaCoverUrl, '/')) {
$metaCoverUrl = '/' . ltrim($metaCoverUrl, '/');
}
return $metaCoverUrl;
}
}
}
} catch (Exception $e) {
error_log('Error parsing metadata: ' . $e->getMessage());
}
}
if (!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);
return '/uploads/track_covers/' . basename($mostRecent);
}
}
}
return '/assets/images/default-track.jpg';
}
// Helper function to extract genre
function extractGenre($track) {
if (!empty($track['genre'])) {
return $track['genre'];
}
if (!empty($track['metadata'])) {
$metadata = is_string($track['metadata']) ? json_decode($track['metadata'], true) : $track['metadata'];
if (isset($metadata['genre'])) return $metadata['genre'];
if (isset($metadata['style'])) return $metadata['style'];
}
return 'Electronic';
}
try {
$result = [
'for_you' => [],
'suggested_creators' => [],
'trending' => []
];
// Build scope condition for "For You" and "Trending"
$scope_condition = '';
if ($scope_filter === 'following' && $user_id) {
$scope_condition = 'AND mt.user_id IN (SELECT following_id FROM user_follows WHERE follower_id = ?)';
}
// For You: Personalized tracks
if ($user_id && $scope_filter === 'following') {
$stmt = $pdo->prepare("
SELECT
mt.id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.metadata,
mt.genre,
mt.image_url,
mt.task_id,
u.name as artist_name,
u.id as artist_id,
COALESCE(play_stats.play_count, 0) as play_count,
COALESCE(like_stats.like_count, 0) as like_count,
COALESCE(comment_stats.comment_count, 0) as comment_count,
(SELECT COUNT(*) FROM track_likes tl WHERE tl.track_id = mt.id AND tl.user_id = ?) as user_liked
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) play_stats ON mt.id = play_stats.track_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
WHERE mt.status = 'complete'
AND mt.audio_url IS NOT NULL
AND mt.audio_url != ''
AND mt.is_public = TRUE
AND mt.user_id IN (SELECT following_id FROM user_follows WHERE follower_id = ?)
$time_condition
ORDER BY COALESCE(mt.published_at, mt.created_at) DESC
LIMIT 5
");
$stmt->execute([$user_id, $user_id]);
} elseif ($user_id) {
$stmt = $pdo->prepare("
SELECT
mt.id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.metadata,
mt.genre,
mt.image_url,
mt.task_id,
u.name as artist_name,
u.id as artist_id,
COALESCE(play_stats.play_count, 0) as play_count,
COALESCE(like_stats.like_count, 0) as like_count,
COALESCE(comment_stats.comment_count, 0) as comment_count,
(SELECT COUNT(*) FROM track_likes tl WHERE tl.track_id = mt.id AND tl.user_id = ?) as user_liked
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) play_stats ON mt.id = play_stats.track_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
WHERE mt.status = 'complete'
AND mt.audio_url IS NOT NULL
AND mt.audio_url != ''
AND mt.is_public = TRUE
$time_condition
ORDER BY (COALESCE(play_stats.play_count, 0) * 2 + COALESCE(like_stats.like_count, 0)) DESC
LIMIT 5
");
$stmt->execute([$user_id]);
} else {
$stmt = $pdo->prepare("
SELECT
mt.id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.metadata,
mt.genre,
mt.image_url,
mt.task_id,
u.name as artist_name,
u.id as artist_id,
COALESCE(play_stats.play_count, 0) as play_count,
COALESCE(like_stats.like_count, 0) as like_count,
COALESCE(comment_stats.comment_count, 0) as comment_count,
0 as user_liked
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) play_stats ON mt.id = play_stats.track_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
WHERE mt.status = 'complete'
AND mt.audio_url IS NOT NULL
AND mt.audio_url != ''
AND mt.is_public = TRUE
$time_condition
ORDER BY (COALESCE(play_stats.play_count, 0) * 2 + COALESCE(like_stats.like_count, 0)) DESC
LIMIT 5
");
$stmt->execute();
}
$for_you_tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Process For You tracks
foreach ($for_you_tracks as &$track) {
if (!empty($track['audio_url'])) {
$track['signed_audio_url'] = getSignedAudioUrl($track['id'], $user_id);
}
$track['image_url'] = resolveTrackImage($track);
$track['genre'] = extractGenre($track);
}
unset($track);
$result['for_you'] = $for_you_tracks;
// Helper function to format profile image URL
function formatProfileImageUrl($profile_image, $name) {
// If no profile image, return null (will use initials)
if (empty($profile_image)) {
return null;
}
// Trim whitespace
$profile_image = trim($profile_image);
// If it's already a full URL (http/https), return as is
if (preg_match('/^https?:\/\//i', $profile_image)) {
return $profile_image;
}
// If it starts with /, it's a valid relative path
if (strpos($profile_image, '/') === 0) {
return $profile_image;
}
// If it looks like a file path but doesn't start with /, try to fix it
// Check if it contains common image extensions
if (preg_match('/\.(jpg|jpeg|png|gif|webp)$/i', $profile_image)) {
// If it's in uploads directory, add leading slash
if (strpos($profile_image, 'uploads/') !== false || strpos($profile_image, 'profile') !== false) {
// Check if it already has a directory structure
if (strpos($profile_image, '/') === false) {
// It's just a filename, assume it's in uploads/profile_images/
return '/uploads/profile_images/' . $profile_image;
} else {
// It has a path but no leading slash
return '/' . ltrim($profile_image, '/');
}
}
// If it starts with a directory name (like 'ds/'), try to construct proper path
if (preg_match('/^[a-z0-9_]+\//i', $profile_image)) {
// This might be a malformed path, try to find it in uploads
$filename = basename($profile_image);
return '/uploads/profile_images/' . $filename;
}
}
// If we can't determine a valid image path, return null (will use initials)
return null;
}
// Suggested Creators (not affected by time/scope filters)
if ($user_id) {
$stmt = $pdo->prepare("
SELECT
u.id,
u.name,
COALESCE(NULLIF(up.profile_image, ''), NULLIF(u.profile_image, ''), NULL) as profile_image,
COALESCE(up.profile_position, 'center center') as profile_position,
COALESCE((SELECT COUNT(*) FROM user_follows WHERE following_id = u.id), 0) as followers_count,
COALESCE((SELECT COUNT(*) FROM music_tracks WHERE user_id = u.id AND status = 'complete'), 0) as track_count
FROM users u
LEFT JOIN user_profiles up ON u.id = up.user_id
WHERE u.id NOT IN (
SELECT COALESCE(following_id, 0) FROM user_follows WHERE follower_id = ?
) AND u.id != ?
AND EXISTS (
SELECT 1 FROM music_tracks mt
WHERE mt.user_id = u.id
AND mt.status = 'complete'
AND mt.is_public = TRUE
)
ORDER BY followers_count DESC, track_count DESC
LIMIT 5
");
$stmt->execute([$user_id, $user_id]);
} else {
$stmt = $pdo->prepare("
SELECT
u.id,
u.name,
COALESCE(NULLIF(up.profile_image, ''), NULLIF(u.profile_image, ''), NULL) as profile_image,
COALESCE(up.profile_position, 'center center') as profile_position,
COALESCE((SELECT COUNT(*) FROM user_follows WHERE following_id = u.id), 0) as followers_count,
COALESCE((SELECT COUNT(*) FROM music_tracks WHERE user_id = u.id AND status = 'complete'), 0) as track_count
FROM users u
LEFT JOIN user_profiles up ON u.id = up.user_id
WHERE EXISTS (
SELECT 1 FROM music_tracks mt
WHERE mt.user_id = u.id
AND mt.status = 'complete'
AND mt.is_public = TRUE
)
ORDER BY followers_count DESC, track_count DESC
LIMIT 5
");
$stmt->execute();
}
$suggested_creators = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Format profile images for suggested creators
foreach ($suggested_creators as &$creator) {
$creator['profile_image'] = formatProfileImageUrl($creator['profile_image'], $creator['name']);
}
unset($creator);
$result['suggested_creators'] = $suggested_creators;
// Trending: Popular tracks (affected by time filter, scope filter)
if ($scope_filter === 'following' && $user_id) {
$stmt = $pdo->prepare("
SELECT
mt.id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.metadata,
mt.genre,
mt.image_url,
mt.task_id,
u.name as artist_name,
u.id as artist_id,
COALESCE(play_stats.play_count, 0) as play_count,
COALESCE(like_stats.like_count, 0) as like_count,
COALESCE(comment_stats.comment_count, 0) as comment_count,
(SELECT COUNT(*) FROM track_likes tl WHERE tl.track_id = mt.id AND tl.user_id = ?) as user_liked
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) play_stats ON mt.id = play_stats.track_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
WHERE mt.status = 'complete'
AND mt.audio_url IS NOT NULL
AND mt.audio_url != ''
AND mt.is_public = TRUE
AND mt.user_id IN (SELECT following_id FROM user_follows WHERE follower_id = ?)
$time_condition
ORDER BY (COALESCE(play_stats.play_count, 0) * 2 + COALESCE(like_stats.like_count, 0)) DESC
LIMIT 5
");
$stmt->execute([$user_id, $user_id]);
} elseif ($user_id) {
$stmt = $pdo->prepare("
SELECT
mt.id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.metadata,
mt.genre,
mt.image_url,
mt.task_id,
u.name as artist_name,
u.id as artist_id,
COALESCE(play_stats.play_count, 0) as play_count,
COALESCE(like_stats.like_count, 0) as like_count,
COALESCE(comment_stats.comment_count, 0) as comment_count,
(SELECT COUNT(*) FROM track_likes tl WHERE tl.track_id = mt.id AND tl.user_id = ?) as user_liked
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) play_stats ON mt.id = play_stats.track_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
WHERE mt.status = 'complete'
AND mt.audio_url IS NOT NULL
AND mt.audio_url != ''
AND mt.is_public = TRUE
$time_condition
ORDER BY (COALESCE(play_stats.play_count, 0) * 2 + COALESCE(like_stats.like_count, 0)) DESC
LIMIT 5
");
$stmt->execute([$user_id]);
} else {
$stmt = $pdo->prepare("
SELECT
mt.id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.metadata,
mt.genre,
mt.image_url,
mt.task_id,
u.name as artist_name,
u.id as artist_id,
COALESCE(play_stats.play_count, 0) as play_count,
COALESCE(like_stats.like_count, 0) as like_count,
COALESCE(comment_stats.comment_count, 0) as comment_count,
0 as user_liked
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) play_stats ON mt.id = play_stats.track_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
WHERE mt.status = 'complete'
AND mt.audio_url IS NOT NULL
AND mt.audio_url != ''
AND mt.is_public = TRUE
$time_condition
ORDER BY (COALESCE(play_stats.play_count, 0) * 2 + COALESCE(like_stats.like_count, 0)) DESC
LIMIT 5
");
$stmt->execute();
}
$trending_tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Process Trending tracks
foreach ($trending_tracks as &$track) {
if (!empty($track['audio_url'])) {
$track['signed_audio_url'] = getSignedAudioUrl($track['id'], $user_id);
}
$track['image_url'] = resolveTrackImage($track);
$track['genre'] = extractGenre($track);
}
unset($track);
$result['trending'] = $trending_tracks;
echo json_encode(['success' => true, 'data' => $result]);
} catch (Exception $e) {
error_log('Error fetching homepage feed: ' . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Error fetching feed data']);
}