![]() 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/ |
<?php
// Configure session before starting it
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 0); // Set to 1 if using HTTPS
ini_set('session.cookie_samesite', 'Lax');
ini_set('session.use_strict_mode', 1);
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
session_start();
// Ensure session cookie is set
if (!isset($_COOKIE[session_name()])) {
setcookie(session_name(), session_id(), time() + 3600, '/', '', false, true);
}
require_once 'config/database.php';
header('Content-Type: application/json');
// Check if user is logged in for certain actions
$user_id = $_SESSION['user_id'] ?? null;
// Get the request data
$input = json_decode(file_get_contents('php://input'), true);
$action = $input['action'] ?? $_POST['action'] ?? $_GET['action'] ?? '';
$track_id = $input['track_id'] ?? $_POST['track_id'] ?? $_GET['track_id'] ?? null;
$user_id_param = $input['user_id'] ?? $_POST['user_id'] ?? $_GET['user_id'] ?? null;
$pdo = getDBConnection();
if (!$pdo) {
echo json_encode(['success' => false, 'message' => 'Database connection failed']);
exit;
}
/**
* Ensure the rating constraint for a table allows values 1-10.
*/
function ensureRatingConstraint(PDO $pdo, string $tableName, string $constraintName = ''): void {
$constraintName = $constraintName ?: $tableName . '_chk_rating';
// Collect existing constraint names from SHOW CREATE TABLE (covers MySQL & MariaDB)
$existingConstraints = [];
try {
$stmt = $pdo->query("SHOW CREATE TABLE `{$tableName}`");
$row = $stmt ? $stmt->fetch(PDO::FETCH_NUM) : null;
if ($row && isset($row[1])) {
$createSql = $row[1];
if (preg_match_all('/CONSTRAINT `([^`]+)`\s+CHECK/i', $createSql, $matches)) {
$existingConstraints = $matches[1];
}
}
} catch (PDOException $e) {
error_log("â Unable to inspect existing constraints for {$tableName}: " . $e->getMessage());
}
$possibleNames = array_unique(array_merge(
$existingConstraints,
[
$constraintName,
"{$tableName}.rating",
"{$tableName}_rating_chk",
"{$tableName}_rating_check"
]
));
foreach ($possibleNames as $name) {
if (!$name) {
continue;
}
try {
$pdo->exec("ALTER TABLE `{$tableName}` DROP CHECK `{$name}`");
} catch (PDOException $e) {
// Some engines (MariaDB) expect DROP CONSTRAINT instead of DROP CHECK
try {
$pdo->exec("ALTER TABLE `{$tableName}` DROP CONSTRAINT `{$name}`");
} catch (PDOException $e2) {
// ignore if still failing (maybe name doesn't exist)
}
}
}
// Ensure column type can store 1-10 without old constraints lingering
try {
$pdo->exec("ALTER TABLE `{$tableName}` MODIFY `rating` TINYINT UNSIGNED NOT NULL");
} catch (PDOException $e) {
error_log("â Unable to adjust rating column on {$tableName}: " . $e->getMessage());
}
// Recreate explicit check if supported (otherwise rely on application validation)
try {
$pdo->exec("ALTER TABLE `{$tableName}` ADD CONSTRAINT `{$constraintName}` CHECK (`rating` BETWEEN 1 AND 10)");
} catch (PDOException $e) {
// Some MySQL/MariaDB versions don't support CHECK; log but continue
error_log("âšī¸ Unable to add rating check on {$tableName}: " . $e->getMessage());
}
}
/**
* Determine if a PDOException was caused by a rating constraint failure.
*/
function isRatingConstraintError(PDOException $e, string $tableName): bool {
if ($e->getCode() === '23000') {
$message = $e->getMessage();
if (stripos($message, $tableName . '.rating') !== false || stripos($message, 'rating') !== false) {
return true;
}
}
return false;
}
try {
switch ($action) {
case 'like':
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to like tracks']);
exit;
}
error_log("Like request - User ID: $user_id, Track ID: $track_id");
// Check if track exists
$stmt = $pdo->prepare("SELECT id FROM music_tracks WHERE id = ?");
$stmt->execute([$track_id]);
$track_exists = $stmt->fetch();
if (!$track_exists) {
error_log("Track not found: $track_id");
echo json_encode(['success' => false, 'message' => 'Track not found']);
exit;
}
// Check if already liked
$stmt = $pdo->prepare("SELECT id FROM track_likes WHERE track_id = ? AND user_id = ?");
$stmt->execute([$track_id, $user_id]);
$existing_like = $stmt->fetch();
if ($existing_like) {
// Unlike
try {
$stmt = $pdo->prepare("DELETE FROM track_likes WHERE track_id = ? AND user_id = ?");
$stmt->execute([$track_id, $user_id]);
error_log("Track unliked successfully - User: $user_id, Track: $track_id");
echo json_encode(['success' => true, 'action' => 'unliked']);
} catch (Exception $e) {
error_log("Unlike error: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Failed to unlike track']);
}
} else {
// Like
try {
$stmt = $pdo->prepare("INSERT INTO track_likes (track_id, user_id) VALUES (?, ?)");
$stmt->execute([$track_id, $user_id]);
error_log("Track liked successfully - User: $user_id, Track: $track_id");
echo json_encode(['success' => true, 'action' => 'liked']);
} catch (Exception $e) {
error_log("Like error: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Failed to like track: ' . $e->getMessage()]);
}
}
break;
case 'view':
// Track view (can be anonymous)
$ip_address = $_SERVER['REMOTE_ADDR'] ?? '';
$stmt = $pdo->prepare("INSERT INTO track_views (track_id, user_id, ip_address) VALUES (?, ?, ?)");
$stmt->execute([$track_id, $user_id, $ip_address]);
echo json_encode(['success' => true]);
break;
case 'play':
// Track play (can be anonymous)
$stmt = $pdo->prepare("INSERT INTO track_plays (track_id, user_id) VALUES (?, ?)");
$stmt->execute([$track_id, $user_id]);
echo json_encode(['success' => true]);
break;
case 'vote_track':
try {
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to vote on tracks']);
exit;
}
if (!$track_id) {
echo json_encode(['success' => false, 'message' => 'Track ID is required']);
exit;
}
$vote_type = $input['vote_type'] ?? 'up';
// Validate vote_type
if (!in_array($vote_type, ['up', 'down'])) {
echo json_encode(['success' => false, 'message' => 'Invalid vote type']);
exit;
}
// Check if track exists
$stmt = $pdo->prepare("SELECT id FROM music_tracks WHERE id = ?");
$stmt->execute([$track_id]);
$track_exists = $stmt->fetch();
if (!$track_exists) {
echo json_encode(['success' => false, 'message' => 'Track not found']);
exit;
}
// Check if already voted
$stmt = $pdo->prepare("SELECT id, vote_type FROM track_votes WHERE track_id = ? AND user_id = ?");
$stmt->execute([$track_id, $user_id]);
$existing_vote = $stmt->fetch();
if ($existing_vote) {
if ($existing_vote['vote_type'] === $vote_type) {
// Remove vote (same vote type clicked again)
$stmt = $pdo->prepare("DELETE FROM track_votes WHERE track_id = ? AND user_id = ?");
$stmt->execute([$track_id, $user_id]);
$action = 'vote_removed';
} else {
// Change vote type
$stmt = $pdo->prepare("UPDATE track_votes SET vote_type = ? WHERE track_id = ? AND user_id = ?");
$stmt->execute([$vote_type, $track_id, $user_id]);
$action = 'vote_changed';
}
} else {
// Add new vote
$stmt = $pdo->prepare("INSERT INTO track_votes (track_id, user_id, vote_type) VALUES (?, ?, ?)");
$stmt->execute([$track_id, $user_id, $vote_type]);
$action = 'vote_added';
}
// Get updated vote count (net votes: up - down) - same as track.php
$vote_count_stmt = $pdo->prepare("
SELECT COALESCE(SUM(CASE WHEN vote_type = 'up' THEN 1 ELSE -1 END), 0) as vote_count
FROM track_votes
WHERE track_id = ?
");
$vote_count_stmt->execute([$track_id]);
$vote_result = $vote_count_stmt->fetch(PDO::FETCH_ASSOC);
$vote_count = (int)($vote_result['vote_count'] ?? 0);
// Get user's current vote
$user_vote_stmt = $pdo->prepare("SELECT vote_type FROM track_votes WHERE track_id = ? AND user_id = ?");
$user_vote_stmt->execute([$track_id, $user_id]);
$user_vote_result = $user_vote_stmt->fetch(PDO::FETCH_ASSOC);
$user_vote = $user_vote_result ? $user_vote_result['vote_type'] : null;
// Debug logging
error_log("Vote API Response: track_id=$track_id, action=$action, vote_count=$vote_count, user_vote=" . ($user_vote ?? 'null'));
echo json_encode([
'success' => true,
'action' => $action,
'vote_count' => $vote_count,
'user_vote' => $user_vote
]);
} catch (PDOException $e) {
error_log("Vote track database error: " . $e->getMessage());
error_log("Vote track SQL error code: " . $e->getCode());
error_log("Vote track error info: " . print_r($e->errorInfo ?? [], true));
// Show more detailed error in development
$error_msg = (defined('DEBUG_MODE') && DEBUG_MODE)
? 'Database error: ' . $e->getMessage() . ' (Code: ' . $e->getCode() . ')'
: 'Database error while processing vote. Please try again.';
echo json_encode([
'success' => false,
'message' => $error_msg
]);
} catch (Exception $e) {
error_log("Vote track error: " . $e->getMessage());
error_log("Vote track error trace: " . $e->getTraceAsString());
$error_msg = (defined('DEBUG_MODE') && DEBUG_MODE)
? 'Error: ' . $e->getMessage()
: 'An error occurred while processing your vote. Please try again.';
echo json_encode([
'success' => false,
'message' => $error_msg
]);
}
break;
case 'share':
// Track share (can be anonymous)
$platform = $input['platform'] ?? 'unknown';
$share_type = $input['share_type'] ?? $platform;
// Create the share record
$stmt = $pdo->prepare("INSERT INTO track_shares (track_id, user_id, share_type, platform, created_at) VALUES (?, ?, ?, ?, NOW())");
$stmt->execute([$track_id, $user_id, $share_type, $platform]);
// Get updated share count
$stmt = $pdo->prepare("SELECT COUNT(*) as share_count FROM track_shares WHERE track_id = ?");
$stmt->execute([$track_id]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$share_count = $result['share_count'] ?? 0;
// Log the share for analytics
error_log("đ Track shared: Track ID $track_id via $platform by user " . ($user_id ?: 'anonymous'));
echo json_encode([
'success' => true,
'message' => "Track shared via $platform!",
'platform' => $platform,
'share_count' => (int)$share_count
]);
break;
case 'follow':
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to follow users']);
exit;
}
if ($user_id == $user_id_param) {
echo json_encode(['success' => false, 'message' => 'You cannot follow yourself']);
exit;
}
// Check if already following
$stmt = $pdo->prepare("SELECT id FROM user_follows WHERE follower_id = ? AND following_id = ?");
$stmt->execute([$user_id, $user_id_param]);
$existing_follow = $stmt->fetch();
if ($existing_follow) {
// Unfollow
$stmt = $pdo->prepare("DELETE FROM user_follows WHERE follower_id = ? AND following_id = ?");
$stmt->execute([$user_id, $user_id_param]);
echo json_encode(['success' => true, 'action' => 'unfollowed']);
} else {
// Follow
$stmt = $pdo->prepare("INSERT INTO user_follows (follower_id, following_id) VALUES (?, ?)");
$stmt->execute([$user_id, $user_id_param]);
$follow_id = $pdo->lastInsertId();
// Create notification for the followed user
try {
// Ensure notification_reads table exists with 'follow' type
$pdo->exec("
CREATE TABLE IF NOT EXISTS notification_reads (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
notification_type ENUM('friend_request', 'like', 'comment', 'artist_rating', 'track_rating', 'follow') NOT NULL,
notification_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_notification (user_id, notification_type, notification_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_user_type (user_id, notification_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
// Try to alter the ENUM if table already exists
try {
$pdo->exec("ALTER TABLE notification_reads MODIFY notification_type ENUM('friend_request', 'like', 'comment', 'artist_rating', 'track_rating', 'follow') NOT NULL");
} catch (PDOException $e) {
// ENUM might already include 'follow' or alter might fail, that's okay
}
} catch (PDOException $e) {
// Table creation might fail if it exists, that's okay
}
echo json_encode(['success' => true, 'action' => 'followed']);
}
break;
case 'comment':
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to comment']);
exit;
}
$comment = $input['comment'] ?? '';
if (empty($comment)) {
echo json_encode(['success' => false, 'message' => 'Comment cannot be empty']);
exit;
}
$stmt = $pdo->prepare("INSERT INTO track_comments (track_id, user_id, comment) VALUES (?, ?, ?)");
$stmt->execute([$track_id, $user_id, $comment]);
echo json_encode(['success' => true, 'message' => 'Comment added successfully']);
break;
case 'get_comments':
$track_id = $_GET['track_id'] ?? null;
$page = (int)($_GET['page'] ?? 1);
$limit = (int)($_GET['limit'] ?? 20);
if (!$track_id) {
echo json_encode(['success' => false, 'message' => 'Track ID required']);
exit;
}
// Validate pagination parameters
if ($page < 1) $page = 1;
if ($limit < 1 || $limit > 100) $limit = 20;
$offset = ($page - 1) * $limit;
// Get total count first
$countStmt = $pdo->prepare("SELECT COUNT(*) as total FROM track_comments WHERE track_id = ?");
$countStmt->execute([$track_id]);
$totalComments = $countStmt->fetch()['total'];
// Get paginated comments
$stmt = $pdo->prepare("
SELECT tc.*, u.name as user_name, u.profile_image
FROM track_comments tc
JOIN users u ON tc.user_id = u.id
WHERE tc.track_id = ?
ORDER BY tc.created_at DESC
LIMIT ? OFFSET ?
");
$stmt->execute([$track_id, $limit, $offset]);
$comments = $stmt->fetchAll();
// Calculate pagination info
$totalPages = ceil($totalComments / $limit);
echo json_encode([
'success' => true,
'comments' => $comments,
'pagination' => [
'current_page' => $page,
'total_pages' => $totalPages,
'total_comments' => $totalComments,
'per_page' => $limit,
'has_next' => $page < $totalPages,
'has_prev' => $page > 1
]
]);
break;
case 'friend_request':
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to send friend requests']);
exit;
}
$friend_id = $input['friend_id'] ?? null;
if (!$friend_id || $user_id == $friend_id) {
echo json_encode(['success' => false, 'message' => 'Invalid friend request']);
exit;
}
// Check if friendship already exists
$stmt = $pdo->prepare("SELECT id, status FROM user_friends WHERE (user_id = ? AND friend_id = ?) OR (user_id = ? AND friend_id = ?)");
$stmt->execute([$user_id, $friend_id, $friend_id, $user_id]);
$existing_friendship = $stmt->fetch();
if ($existing_friendship) {
if ($existing_friendship['status'] === 'accepted') {
// Remove friendship
$stmt = $pdo->prepare("DELETE FROM user_friends WHERE id = ?");
$stmt->execute([$existing_friendship['id']]);
echo json_encode(['success' => true, 'action' => 'unfriended']);
} else {
echo json_encode(['success' => false, 'message' => 'Friend request already sent']);
}
} else {
// Send friend request
$stmt = $pdo->prepare("INSERT INTO user_friends (user_id, friend_id, status) VALUES (?, ?, 'pending')");
$stmt->execute([$user_id, $friend_id]);
echo json_encode(['success' => true, 'action' => 'friend_request_sent']);
}
break;
case 'accept_friend':
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to accept friend requests']);
exit;
}
$friend_id = $input['friend_id'] ?? null;
if (!$friend_id) {
echo json_encode(['success' => false, 'message' => 'Invalid friend ID']);
exit;
}
// Update friend request status
$stmt = $pdo->prepare("UPDATE user_friends SET status = 'accepted' WHERE friend_id = ? AND user_id = ? AND status = 'pending'");
$stmt->execute([$user_id, $friend_id]);
if ($stmt->rowCount() > 0) {
echo json_encode(['success' => true, 'action' => 'friend_accepted']);
} else {
echo json_encode(['success' => false, 'message' => 'No pending friend request found']);
}
break;
case 'decline_friend':
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to decline friend requests']);
exit;
}
$friend_id = $input['friend_id'] ?? null;
if (!$friend_id) {
echo json_encode(['success' => false, 'message' => 'Invalid friend ID']);
exit;
}
// Delete friend request
$stmt = $pdo->prepare("DELETE FROM user_friends WHERE friend_id = ? AND user_id = ? AND status = 'pending'");
$stmt->execute([$user_id, $friend_id]);
if ($stmt->rowCount() > 0) {
echo json_encode(['success' => true, 'action' => 'friend_declined']);
} else {
echo json_encode(['success' => false, 'message' => 'No pending friend request found']);
}
break;
case 'send_message':
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to send messages']);
exit;
}
$receiver_id = $input['receiver_id'] ?? null;
$message = $input['message'] ?? '';
if (!$receiver_id || empty($message)) {
echo json_encode(['success' => false, 'message' => 'Receiver ID and message are required']);
exit;
}
if ($user_id == $receiver_id) {
echo json_encode(['success' => false, 'message' => 'You cannot send messages to yourself']);
exit;
}
// Insert message
$stmt = $pdo->prepare("INSERT INTO user_messages (sender_id, receiver_id, message) VALUES (?, ?, ?)");
$stmt->execute([$user_id, $receiver_id, $message]);
echo json_encode(['success' => true, 'message' => 'Message sent successfully']);
break;
case 'get_messages':
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to view messages']);
exit;
}
$other_user_id = $_GET['user_id'] ?? null;
if (!$other_user_id) {
echo json_encode(['success' => false, 'message' => 'User ID required']);
exit;
}
// Get messages between two users
$stmt = $pdo->prepare("
SELECT um.*, u.name as sender_name
FROM user_messages um
JOIN users u ON um.sender_id = u.id
WHERE (um.sender_id = ? AND um.receiver_id = ?) OR (um.sender_id = ? AND um.receiver_id = ?)
ORDER BY um.created_at ASC
");
$stmt->execute([$user_id, $other_user_id, $other_user_id, $user_id]);
$messages = $stmt->fetchAll();
// Mark messages as read
$stmt = $pdo->prepare("UPDATE user_messages SET is_read = TRUE WHERE sender_id = ? AND receiver_id = ? AND is_read = FALSE");
$stmt->execute([$other_user_id, $user_id]);
echo json_encode(['success' => true, 'messages' => $messages]);
break;
case 'upload_profile_image':
error_log("đĩ Profile image upload requested for user: " . ($user_id ?? 'not logged in'));
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to upload images']);
exit;
}
// Handle file upload
if (!isset($_FILES['profile_image']) || $_FILES['profile_image']['error'] !== UPLOAD_ERR_OK) {
error_log("đĩ Profile image upload error: " . ($_FILES['profile_image']['error'] ?? 'no file'));
echo json_encode(['success' => false, 'message' => 'No image file uploaded or upload error']);
exit;
}
$file = $_FILES['profile_image'];
// SECURITY: Enhanced file upload validation
require_once __DIR__ . '/includes/security.php';
$validation = validateFileUpload($file, ['jpg', 'jpeg', 'png', 'gif', 'webp'], 5 * 1024 * 1024); // 5MB max
if (!$validation['valid']) {
error_log("SECURITY: Invalid profile image upload attempt from user $user_id: " . ($validation['error'] ?? 'unknown error'));
echo json_encode(['success' => false, 'message' => 'Invalid file. ' . ($validation['error'] ?? 'Please upload a valid image file.')]);
exit;
}
// Create uploads directory if it doesn't exist
$upload_dir = 'uploads/profile_images/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
// Use sanitized filename from validation
$filename = 'profile_' . $user_id . '_' . time() . '_' . $validation['filename'];
$filepath = $upload_dir . $filename;
$db_path = '/' . $filepath; // Add leading slash for database
if (move_uploaded_file($file['tmp_name'], $filepath)) {
// Compress and optimize the image
require_once __DIR__ . '/utils/image_compression.php';
$compression_result = compressProfileImage($filepath);
if ($compression_result['success']) {
// Update filename if format changed (e.g., PNG to JPEG)
if (!empty($compression_result['format_changed']) && $compression_result['format_changed']) {
$filename = basename($compression_result['filepath']);
$filepath = $upload_dir . $filename;
$db_path = '/' . $filepath;
}
error_log("đĩ Profile image compressed: " . ($compression_result['message'] ?? 'Success'));
} else {
error_log("đĩ Image compression warning: " . ($compression_result['error'] ?? 'Unknown error'));
// Continue even if compression fails - original image is still usable
}
// Ensure user_profiles record exists
$stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
$stmt->execute([$user_id]);
if ($stmt->fetchColumn() == 0) {
// Create user_profiles record if it doesn't exist
$stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
$stmt->execute([$user_id]);
error_log("đĩ Created user_profiles record for user: " . $user_id);
}
// Update user profile in database
$stmt = $pdo->prepare("UPDATE user_profiles SET profile_image = ? WHERE user_id = ?");
$stmt->execute([$db_path, $user_id]);
error_log("đĩ Profile image uploaded successfully: " . $filepath);
echo json_encode(['success' => true, 'data' => ['image_url' => $db_path]]);
} else {
error_log("đĩ Profile image upload failed to move file");
echo json_encode(['success' => false, 'message' => 'Failed to save image file']);
}
break;
case 'upload_cover_image':
error_log("đĩ Cover image upload requested for user: " . ($user_id ?? 'not logged in'));
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to upload images']);
exit;
}
// Ensure user_profiles record exists
$stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
$stmt->execute([$user_id]);
if ($stmt->fetchColumn() == 0) {
// Create user_profiles record if it doesn't exist
$stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
$stmt->execute([$user_id]);
error_log("đĩ Created user_profiles record for user: " . $user_id);
}
// Handle file upload
if (!isset($_FILES['cover_image']) || $_FILES['cover_image']['error'] !== UPLOAD_ERR_OK) {
error_log("đĩ Cover image upload error: " . ($_FILES['cover_image']['error'] ?? 'no file'));
echo json_encode(['success' => false, 'message' => 'No image file uploaded or upload error']);
exit;
}
$file = $_FILES['cover_image'];
$allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($file['type'], $allowed_types)) {
echo json_encode(['success' => false, 'message' => 'Invalid file type. Only JPEG, PNG, GIF, and WebP are allowed']);
exit;
}
if ($file['size'] > 5 * 1024 * 1024) { // 5MB limit
echo json_encode(['success' => false, 'message' => 'File size too large. Maximum 5MB allowed']);
exit;
}
// Create uploads directory if it doesn't exist
// SECURITY: Enhanced file upload validation
require_once __DIR__ . '/includes/security.php';
$validation = validateFileUpload($file, ['jpg', 'jpeg', 'png', 'gif', 'webp'], 5 * 1024 * 1024); // 5MB max
if (!$validation['valid']) {
error_log("SECURITY: Invalid cover image upload attempt from user $user_id: " . ($validation['error'] ?? 'unknown error'));
echo json_encode(['success' => false, 'message' => 'Invalid file. ' . ($validation['error'] ?? 'Please upload a valid image file.')]);
exit;
}
$upload_dir = 'uploads/cover_images/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
// Use sanitized filename from validation
$filename = 'cover_' . $user_id . '_' . time() . '_' . $validation['filename'];
$filepath = $upload_dir . $filename;
$db_path = '/' . $filepath; // Add leading slash for database
if (move_uploaded_file($file['tmp_name'], $filepath)) {
// Compress and optimize the image (cover images can be larger)
require_once __DIR__ . '/utils/image_compression.php';
$compression_result = compressImage($filepath, [
'max_width' => 1920,
'max_height' => 1080,
'quality' => 85,
'convert_png_to_jpeg' => true,
'max_file_size' => 500 * 1024, // 500KB max
]);
if ($compression_result['success']) {
// Update filename if format changed (e.g., PNG to JPEG)
if (!empty($compression_result['format_changed']) && $compression_result['format_changed']) {
$filename = basename($compression_result['filepath']);
$filepath = $upload_dir . $filename;
$db_path = '/' . $filepath;
}
error_log("đĩ Cover image compressed: " . ($compression_result['message'] ?? 'Success'));
} else {
error_log("đĩ Image compression warning: " . ($compression_result['error'] ?? 'Unknown error'));
// Continue even if compression fails - original image is still usable
}
// Update user profile in database
$stmt = $pdo->prepare("UPDATE user_profiles SET cover_image = ? WHERE user_id = ?");
$stmt->execute([$db_path, $user_id]);
error_log("đĩ Cover image uploaded successfully: " . $filepath);
echo json_encode(['success' => true, 'data' => ['image_url' => $db_path]]);
} else {
error_log("đĩ Cover image upload failed to move file");
echo json_encode(['success' => false, 'message' => 'Failed to save image file']);
}
break;
case 'save_cover_position':
error_log("đĩ Cover position save requested for user: " . ($user_id ?? 'not logged in'));
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to save cover position']);
exit;
}
$position = trim($_POST['position'] ?? 'center center');
$artist_id = (int)($_POST['artist_id'] ?? 0);
// Validate position format (should be "center X%" where X is 0-100, allows decimals)
if (!preg_match('/^center\s+\d+(?:\.\d+)?%$/', $position) && $position !== 'center center') {
error_log("đĩ Invalid cover position format: " . $position);
echo json_encode(['success' => false, 'message' => 'Invalid position format: ' . $position]);
exit;
}
// Normalize "center center" to "center 50%"
if ($position === 'center center') {
$position = 'center 50%';
}
// Verify the user owns this profile
if ($artist_id !== $user_id) {
error_log("đĩ Security: User $user_id attempted to modify profile $artist_id");
echo json_encode(['success' => false, 'message' => 'You can only modify your own profile']);
exit;
}
try {
// Ensure user_profiles record exists
$stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
$stmt->execute([$user_id]);
if ($stmt->fetchColumn() == 0) {
// Create user_profiles record if it doesn't exist
$stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
$stmt->execute([$user_id]);
error_log("đĩ Created user_profiles record for user: " . $user_id);
}
// Update cover position in database
$stmt = $pdo->prepare("UPDATE user_profiles SET cover_position = ?, updated_at = NOW() WHERE user_id = ?");
$result = $stmt->execute([$position, $user_id]);
if (!$result) {
throw new Exception('Database update failed');
}
error_log("đĩ Cover position saved successfully: " . $position . " for user: " . $user_id);
echo json_encode(['success' => true, 'data' => ['position' => $position]]);
} catch (Exception $e) {
error_log("đĩ Error saving cover position: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Failed to save cover position. Please try again.']);
}
break;
case 'save_profile_position':
error_log("đĩ Profile position save requested for user: " . ($user_id ?? 'not logged in'));
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to save profile position']);
exit;
}
$position = $_POST['position'] ?? 'center center';
$artist_id = (int)($_POST['artist_id'] ?? 0);
// Verify the user owns this profile
if ($artist_id !== $user_id) {
echo json_encode(['success' => false, 'message' => 'You can only modify your own profile']);
exit;
}
// Ensure user_profiles record exists
$stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
$stmt->execute([$user_id]);
if ($stmt->fetchColumn() == 0) {
// Create user_profiles record if it doesn't exist
$stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
$stmt->execute([$user_id]);
error_log("đĩ Created user_profiles record for user: " . $user_id);
}
// Update profile position in database
$stmt = $pdo->prepare("UPDATE user_profiles SET profile_position = ? WHERE user_id = ?");
$stmt->execute([$position, $user_id]);
// Update session so header reflects the change immediately
$_SESSION['profile_position'] = $position;
error_log("đĩ Profile position saved successfully: " . $position);
echo json_encode(['success' => true, 'data' => ['position' => $position]]);
break;
case 'update_profile':
error_log("đĩ Profile update requested for user: " . ($user_id ?? 'not logged in'));
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to update profile']);
exit;
}
$field = $_POST['field'] ?? $input['field'] ?? '';
$value = $_POST['value'] ?? $input['value'] ?? '';
if (empty($field)) {
echo json_encode(['success' => false, 'message' => 'Field name is required']);
exit;
}
// Ensure user_profiles record exists
$stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
$stmt->execute([$user_id]);
if ($stmt->fetchColumn() == 0) {
// Create user_profiles record if it doesn't exist
$stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
$stmt->execute([$user_id]);
error_log("đĩ Created user_profiles record for user: " . $user_id);
}
// Map field names to database columns
$field_mapping = [
'highlights' => 'artist_highlights',
'bio' => 'bio',
'influences' => 'influences',
'equipment' => 'equipment',
'achievements' => 'achievements',
'artist_statement' => 'artist_statement'
];
$db_field = $field_mapping[$field] ?? $field;
// Update profile field in database
$stmt = $pdo->prepare("UPDATE user_profiles SET $db_field = ?, updated_at = NOW() WHERE user_id = ?");
$stmt->execute([$value, $user_id]);
error_log("đĩ Profile field '$field' updated successfully for user: " . $user_id);
echo json_encode(['success' => true, 'data' => ['field' => $field, 'value' => $value]]);
break;
case 'submit_rating':
error_log("đĩ Rating submission requested for artist: " . ($_POST['artist_id'] ?? 'not specified'));
error_log("đĩ POST data: " . print_r($_POST, true));
if (!$user_id) {
error_log("đĩ Rating submission failed: User not logged in");
echo json_encode(['success' => false, 'message' => 'Please log in to submit a rating']);
exit;
}
$artist_id = (int)($_POST['artist_id'] ?? 0);
$rating = (int)($_POST['rating'] ?? 0);
$comment = $_POST['comment'] ?? '';
error_log("đĩ Parsed rating data: artist_id=$artist_id, rating=$rating, user_id=$user_id");
if (!$artist_id) {
error_log("đĩ Rating submission failed: Invalid artist_id");
echo json_encode(['success' => false, 'message' => 'Invalid artist ID']);
exit;
}
if ($rating < 1 || $rating > 10) {
error_log("đĩ Rating submission failed: Invalid rating value ($rating)");
echo json_encode(['success' => false, 'message' => 'Invalid rating. Please select a rating between 1 and 10.']);
exit;
}
// Users can't rate themselves
if ($artist_id === $user_id) {
error_log("đĩ Rating submission failed: User trying to rate themselves");
echo json_encode(['success' => false, 'message' => 'You cannot rate yourself']);
exit;
}
try {
// Check if table exists, if not create it
$table_check = $pdo->query("SHOW TABLES LIKE 'artist_ratings'");
if ($table_check->rowCount() == 0) {
// Create table without foreign keys first to avoid constraint issues
$pdo->exec("
CREATE TABLE artist_ratings (
id INT AUTO_INCREMENT PRIMARY KEY,
artist_id INT NOT NULL,
user_id INT NOT NULL,
rating INT NOT NULL,
comment TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY unique_rating (artist_id, user_id),
INDEX idx_artist_id (artist_id),
INDEX idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
");
error_log("đĩ Created artist_ratings table");
}
// Ensure rating constraint allows full 1-10 range
ensureRatingConstraint($pdo, 'artist_ratings', 'chk_artist_rating');
// Verify artist and user exist
$check_artist = $pdo->prepare("SELECT id FROM users WHERE id = ?");
$check_artist->execute([$artist_id]);
if (!$check_artist->fetch()) {
throw new Exception("Artist with ID $artist_id does not exist");
}
$check_user = $pdo->prepare("SELECT id FROM users WHERE id = ?");
$check_user->execute([$user_id]);
if (!$check_user->fetch()) {
throw new Exception("User with ID $user_id does not exist");
}
// Insert or update rating with a retry if constraint needs adjustment
$insertSql = "
INSERT INTO artist_ratings (artist_id, user_id, rating, comment)
VALUES (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
rating = VALUES(rating),
comment = VALUES(comment),
updated_at = CURRENT_TIMESTAMP
";
$insertParams = [$artist_id, $user_id, $rating, $comment];
$insertSuccess = false;
for ($attempt = 0; $attempt < 2; $attempt++) {
try {
$stmt = $pdo->prepare($insertSql);
$stmt->execute($insertParams);
$insertSuccess = true;
break;
} catch (PDOException $e) {
if ($attempt === 0 && isRatingConstraintError($e, 'artist_ratings')) {
error_log("đĩ Detected outdated artist rating constraint, attempting to fix...");
ensureRatingConstraint($pdo, 'artist_ratings', 'chk_artist_rating');
continue;
}
throw $e;
}
}
if (!$insertSuccess) {
throw new Exception("Failed to save rating after constraint adjustment");
}
// Get updated average rating and count
$stmt = $pdo->prepare("SELECT AVG(rating) as avg_rating, COUNT(*) as total_ratings FROM artist_ratings WHERE artist_id = ?");
$stmt->execute([$artist_id]);
$rating_data = $stmt->fetch();
// Verify the rating was actually saved
$verify_stmt = $pdo->prepare("SELECT rating, comment FROM artist_ratings WHERE artist_id = ? AND user_id = ?");
$verify_stmt->execute([$artist_id, $user_id]);
$saved_rating = $verify_stmt->fetch();
if (!$saved_rating) {
throw new Exception("Rating was not saved to database");
}
error_log("đĩ Rating submitted successfully: $rating/10 for artist $artist_id by user $user_id");
error_log("đĩ Verified saved rating: " . print_r($saved_rating, true));
echo json_encode([
'success' => true,
'data' => [
'rating' => $rating,
'average_rating' => round($rating_data['avg_rating'], 1),
'total_ratings' => $rating_data['total_ratings']
]
]);
} catch (PDOException $e) {
error_log("đĩ Rating submission PDO error: " . $e->getMessage());
error_log("đĩ PDO error code: " . $e->getCode());
error_log("đĩ Stack trace: " . $e->getTraceAsString());
if ($e->getCode() === '23000' || (isset($e->errorInfo[2]) && strpos($e->errorInfo[2], 'artist_ratings.rating') !== false)) {
echo json_encode([
'success' => false,
'message' => 'Invalid rating value. Please choose a whole number between 1 and 10.'
]);
} else {
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
}
} catch (Exception $e) {
error_log("đĩ Rating submission failed: " . $e->getMessage());
error_log("đĩ Stack trace: " . $e->getTraceAsString());
echo json_encode(['success' => false, 'message' => 'Failed to submit rating: ' . $e->getMessage()]);
}
break;
case 'submit_track_rating':
error_log("đĩ Track rating submission requested for track: " . ($_POST['track_id'] ?? 'not specified'));
if (!$user_id) {
echo json_encode(['success' => false, 'message' => 'Please log in to submit a track rating']);
exit;
}
$track_id = (int)($_POST['track_id'] ?? 0);
$rating = (int)($_POST['rating'] ?? 0);
$comment = $_POST['comment'] ?? '';
error_log("đĩ Track rating data: track_id=$track_id, rating=$rating, user_id=$user_id");
if (!$track_id) {
error_log("đĩ Track rating failed: Invalid track_id");
echo json_encode(['success' => false, 'message' => 'Invalid track ID']);
exit;
}
if ($rating < 1 || $rating > 10) {
error_log("đĩ Track rating failed: Invalid rating value ($rating)");
echo json_encode(['success' => false, 'message' => 'Invalid rating. Please select a rating between 1 and 10.']);
exit;
}
try {
// Check if track exists and user owns it
$stmt = $pdo->prepare("SELECT user_id FROM music_tracks WHERE id = ?");
$stmt->execute([$track_id]);
$track = $stmt->fetch();
if (!$track) {
error_log("đĩ Track rating failed: Track not found");
echo json_encode(['success' => false, 'message' => 'Track not found']);
exit;
}
// Ensure rating constraint allows full 1-10 range
ensureRatingConstraint($pdo, 'track_ratings', 'chk_track_rating');
// Insert or update rating with retry if needed
$trackInsertSql = "
INSERT INTO track_ratings (track_id, user_id, rating, comment)
VALUES (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
rating = VALUES(rating),
comment = VALUES(comment),
updated_at = CURRENT_TIMESTAMP
";
$trackInsertParams = [$track_id, $user_id, $rating, $comment];
$trackInsertSuccess = false;
for ($attempt = 0; $attempt < 2; $attempt++) {
try {
$stmt = $pdo->prepare($trackInsertSql);
$stmt->execute($trackInsertParams);
$trackInsertSuccess = true;
break;
} catch (PDOException $insertException) {
if ($attempt === 0 && isRatingConstraintError($insertException, 'track_ratings')) {
error_log("đĩ Detected outdated track rating constraint, attempting to fix...");
ensureRatingConstraint($pdo, 'track_ratings', 'chk_track_rating');
continue;
}
throw $insertException;
}
}
if (!$trackInsertSuccess) {
throw new Exception("Failed to save track rating after constraint adjustment");
}
// Verify the rating was actually saved BEFORE calculating averages
error_log("đĩ Verifying rating was saved: track_id=$track_id, user_id=$user_id");
$verify_stmt = $pdo->prepare("SELECT id, rating, comment, created_at, updated_at FROM track_ratings WHERE track_id = ? AND user_id = ?");
$verify_stmt->execute([$track_id, $user_id]);
$saved_rating = $verify_stmt->fetch();
if (!$saved_rating) {
error_log("đĩ ERROR: Verification query returned no rows!");
error_log("đĩ Attempting to check all ratings for this track...");
$all_ratings_stmt = $pdo->prepare("SELECT * FROM track_ratings WHERE track_id = ?");
$all_ratings_stmt->execute([$track_id]);
$all_ratings = $all_ratings_stmt->fetchAll();
error_log("đĩ All ratings for track $track_id: " . print_r($all_ratings, true));
throw new Exception("Rating was not saved to database - verification failed");
}
error_log("đĩ Verification successful! Saved rating: " . print_r($saved_rating, true));
// Check if the saved rating matches what we tried to save
if ($saved_rating['rating'] != $rating) {
error_log("đĩ WARNING: Saved rating ({$saved_rating['rating']}) does not match submitted rating ($rating)!");
}
// Get updated average rating and count
$stmt = $pdo->prepare("SELECT AVG(rating) as avg_rating, COUNT(*) as total_ratings FROM track_ratings WHERE track_id = ?");
$stmt->execute([$track_id]);
$rating_data = $stmt->fetch();
error_log("đĩ Track rating submitted successfully: $rating/10 for track $track_id by user $user_id");
error_log("đĩ Average rating: {$rating_data['avg_rating']}, Total ratings: {$rating_data['total_ratings']}");
echo json_encode([
'success' => true,
'data' => [
'rating' => $rating,
'average_rating' => round($rating_data['avg_rating'], 1),
'total_ratings' => $rating_data['total_ratings']
]
]);
} catch (PDOException $e) {
error_log("đĩ Track rating submission PDO error: " . $e->getMessage());
error_log("đĩ PDO error code: " . $e->getCode());
error_log("đĩ Stack trace: " . $e->getTraceAsString());
if ($e->getCode() === '23000' || (isset($e->errorInfo[2]) && strpos($e->errorInfo[2], 'track_ratings.rating') !== false)) {
echo json_encode([
'success' => false,
'message' => 'Invalid rating value. Please choose a whole number between 1 and 10.'
]);
} else {
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
}
} catch (Exception $e) {
error_log("đĩ Track rating submission failed: " . $e->getMessage());
error_log("đĩ Stack trace: " . $e->getTraceAsString());
echo json_encode(['success' => false, 'message' => 'Failed to submit track rating: ' . $e->getMessage()]);
}
break;
case 'get_track_ratings':
error_log("đĩ Getting track ratings for track: " . ($_GET['track_id'] ?? $_POST['track_id'] ?? 'not specified'));
$track_id = (int)($_GET['track_id'] ?? $_POST['track_id'] ?? 0);
if (!$track_id) {
echo json_encode(['success' => false, 'message' => 'Track ID required']);
exit;
}
try {
// Get all ratings for this track with user information
$stmt = $pdo->prepare("
SELECT
tr.id,
tr.rating,
tr.comment,
tr.created_at,
tr.updated_at,
u.id as user_id,
u.name as user_name,
u.profile_image
FROM track_ratings tr
JOIN users u ON tr.user_id = u.id
WHERE tr.track_id = ?
ORDER BY tr.created_at DESC
");
$stmt->execute([$track_id]);
$ratings = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get average rating and count
$avg_stmt = $pdo->prepare("
SELECT
AVG(rating) as average_rating,
COUNT(*) as total_ratings
FROM track_ratings
WHERE track_id = ?
");
$avg_stmt->execute([$track_id]);
$rating_stats = $avg_stmt->fetch();
error_log("đĩ Found " . count($ratings) . " ratings for track $track_id");
echo json_encode([
'success' => true,
'data' => [
'ratings' => $ratings,
'average_rating' => round($rating_stats['average_rating'] ?? 0, 1),
'total_ratings' => $rating_stats['total_ratings'] ?? 0
]
]);
} catch (Exception $e) {
error_log("đĩ Error fetching track ratings: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Failed to fetch ratings: ' . $e->getMessage()]);
}
break;
case 'get_user_track_rating':
error_log("đĩ Getting user's track rating for track: " . ($_GET['track_id'] ?? $_POST['track_id'] ?? 'not specified'));
if (!$user_id) {
echo json_encode(['success' => true, 'data' => ['rating' => null, 'comment' => null]]);
exit;
}
$track_id = (int)($_GET['track_id'] ?? $_POST['track_id'] ?? 0);
if (!$track_id) {
echo json_encode(['success' => false, 'message' => 'Track ID required']);
exit;
}
try {
$stmt = $pdo->prepare("
SELECT rating, comment
FROM track_ratings
WHERE track_id = ? AND user_id = ?
");
$stmt->execute([$track_id, $user_id]);
$user_rating = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user_rating) {
error_log("đĩ Found user rating: " . print_r($user_rating, true));
echo json_encode([
'success' => true,
'data' => [
'rating' => (int)$user_rating['rating'],
'comment' => $user_rating['comment'] ?? ''
]
]);
} else {
error_log("đĩ No existing rating found for user $user_id on track $track_id");
echo json_encode([
'success' => true,
'data' => [
'rating' => null,
'comment' => null
]
]);
}
} catch (Exception $e) {
error_log("đĩ Failed to fetch user track rating: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Failed to fetch user track rating: ' . $e->getMessage()]);
}
break;
case 'get_user_artist_rating':
error_log("đĩ Getting user's artist rating for artist: " . ($_GET['artist_id'] ?? $_POST['artist_id'] ?? 'not specified'));
if (!$user_id) {
echo json_encode(['success' => true, 'data' => ['rating' => null, 'comment' => null]]);
exit;
}
$artist_id = (int)($_GET['artist_id'] ?? $_POST['artist_id'] ?? 0);
if (!$artist_id) {
echo json_encode(['success' => false, 'message' => 'Artist ID required']);
exit;
}
try {
$stmt = $pdo->prepare("
SELECT rating, comment
FROM artist_ratings
WHERE artist_id = ? AND user_id = ?
");
$stmt->execute([$artist_id, $user_id]);
$user_rating = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user_rating) {
error_log("đĩ Found user artist rating: " . print_r($user_rating, true));
echo json_encode([
'success' => true,
'data' => [
'rating' => (int)$user_rating['rating'],
'comment' => $user_rating['comment'] ?? ''
]
]);
} else {
error_log("đĩ No existing artist rating found for user $user_id on artist $artist_id");
echo json_encode([
'success' => true,
'data' => [
'rating' => null,
'comment' => null
]
]);
}
} catch (Exception $e) {
error_log("đĩ Failed to fetch user artist rating: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Failed to fetch artist rating: ' . $e->getMessage()]);
}
break;
case 'get_artist_rating_summary':
$artist_id = (int)($_GET['artist_id'] ?? $_POST['artist_id'] ?? 0);
if (!$artist_id) {
echo json_encode(['success' => false, 'message' => 'Artist ID required']);
exit;
}
try {
$stmt = $pdo->prepare("
SELECT
COALESCE(AVG(rating), 0) as average_rating,
COUNT(*) as total_ratings
FROM artist_ratings
WHERE artist_id = ?
");
$stmt->execute([$artist_id]);
$summary = $stmt->fetch(PDO::FETCH_ASSOC) ?: ['average_rating' => 0, 'total_ratings' => 0];
echo json_encode([
'success' => true,
'data' => [
'average_rating' => round((float)$summary['average_rating'], 1),
'total_ratings' => (int)$summary['total_ratings']
]
]);
} catch (Exception $e) {
error_log("đĩ Failed to fetch artist rating summary: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Failed to fetch artist summary: ' . $e->getMessage()]);
}
break;
default:
echo json_encode(['success' => false, 'message' => 'Invalid action']);
break;
}
} catch (Exception $e) {
error_log("Social API error: " . $e->getMessage());
error_log("Social API error trace: " . $e->getTraceAsString());
// Only show detailed error in development, generic message in production
$error_message = (defined('DEBUG_MODE') && DEBUG_MODE)
? 'An error occurred: ' . $e->getMessage()
: 'An error occurred. Please try again.';
echo json_encode(['success' => false, 'message' => $error_message]);
}
?>