![]() 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 '../config/database.php';
$crate_id = isset($_GET['crate_id']) ? (int)$_GET['crate_id'] : null;
$is_public_request = isset($_GET['public']) && $_GET['public'] == '1';
if (!$crate_id) {
echo json_encode(['success' => false, 'error' => 'Crate ID is required']);
exit;
}
// Helper function to convert external URLs to proxy endpoint
function convertToProxyUrl($audioUrl, $taskId) {
if (empty($audioUrl)) return $audioUrl;
// If URL is external, convert to proxy
if (strpos($audioUrl, 'http') === 0 ||
strpos($audioUrl, 'api.box') !== false ||
strpos($audioUrl, 'apiboxfiles.erweima.ai') !== false) {
if (!empty($taskId)) {
return '/utils/audiofiles.php?id=' . urlencode($taskId);
}
}
return $audioUrl;
}
try {
$pdo = getDBConnection();
// Check if is_description_public column exists
$checkDescCol = $pdo->query("SHOW COLUMNS FROM artist_playlists LIKE 'is_description_public'");
$hasDescPublicColumn = $checkDescCol->rowCount() > 0;
$descPublicField = $hasDescPublicColumn ? ", is_description_public" : "";
// Check if this is a public crate request (for artist profile viewing)
if ($is_public_request) {
// For public requests, only allow access to public crates
$stmt = $pdo->prepare("SELECT id, name, description, user_id, is_public $descPublicField FROM artist_playlists WHERE id = ? AND is_public = 1");
$stmt->execute([$crate_id]);
$crate = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$crate) {
echo json_encode(['success' => false, 'error' => 'Crate not found or is private']);
exit;
}
// Hide description if not public
if (!$hasDescPublicColumn || $crate['is_description_public'] == 0) {
$crate['description'] = null;
}
} else {
// For authenticated requests, verify crate belongs to user
if (!isset($_SESSION['user_id'])) {
echo json_encode(['success' => false, 'error' => 'Not authenticated']);
exit;
}
$stmt = $pdo->prepare("
SELECT
ap.id,
ap.name,
ap.description,
ap.user_id,
ap.is_public $descPublicField,
u.name as artist_name
FROM artist_playlists ap
JOIN users u ON ap.user_id = u.id
WHERE ap.id = ? AND ap.user_id = ?
");
$stmt->execute([$crate_id, $_SESSION['user_id']]);
$crate = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$crate) {
echo json_encode(['success' => false, 'error' => 'Crate not found or access denied']);
exit;
}
// Set default if column doesn't exist
if (!$hasDescPublicColumn) {
$crate['is_description_public'] = 1;
}
}
// Check if is_public column exists in playlist_tracks
$checkColumn = $pdo->query("SHOW COLUMNS FROM playlist_tracks LIKE 'is_public'");
$hasIsPublicColumn = $checkColumn->rowCount() > 0;
// Get tracks in crate (use LEFT JOIN to handle empty crates)
// For public requests, only show tracks marked as public in the crate
$publicFilter = "";
if ($is_public_request && $hasIsPublicColumn) {
$publicFilter = "AND (pt.is_public = 1 OR pt.is_public IS NULL)";
}
// Check if user is authenticated (for purchase check)
$current_user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
$query = "
SELECT
mt.id,
mt.title,
mt.prompt,
mt.audio_url,
mt.task_id,
mt.duration,
mt.created_at,
mt.user_id,
mt.metadata,
mt.price,
mt.is_public as track_is_public,
" . ($hasIsPublicColumn ? "COALESCE(pt.is_public, 1)" : "1") . " as crate_track_public,
u.name as artist_name,
pt.position,
CASE
WHEN mt.duration >= 300 THEN 2.5
WHEN mt.duration IS NULL OR mt.duration = 0 THEN 0
ELSE mt.duration * 0.5 / 60
END as set_duration_minutes" .
($current_user_id ? ",
CASE WHEN mt.user_id = ? THEN 1 ELSE 0 END as user_owns_track,
CASE WHEN EXISTS(SELECT 1 FROM track_purchases WHERE user_id = ? AND track_id = mt.id) THEN 1 ELSE 0 END as user_purchased_track" : ",
0 as user_owns_track,
0 as user_purchased_track") . "
FROM playlist_tracks pt
LEFT JOIN music_tracks mt ON pt.track_id = mt.id
LEFT JOIN users u ON mt.user_id = u.id
WHERE pt.playlist_id = ?
AND (mt.status = 'complete' OR mt.status IS NULL)
$publicFilter
ORDER BY pt.position ASC, pt.added_at ASC
";
$stmt = $pdo->prepare($query);
if ($current_user_id) {
$stmt->execute([$current_user_id, $current_user_id, $crate_id]);
} else {
$stmt->execute([$crate_id]);
}
$tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Filter out any null tracks (in case of orphaned playlist_tracks)
$tracks = array_filter($tracks, function($track) {
return !empty($track['id']);
});
$tracks = array_values($tracks); // Re-index array
// SECURITY: For PUBLIC requests, NEVER expose audio URLs or task IDs
// Client must use get_audio_token.php to get signed URLs for playback
if ($is_public_request) {
foreach ($tracks as &$track) {
// Remove sensitive audio data - only keep metadata for display
unset($track['audio_url']);
unset($track['task_id']);
unset($track['prompt']);
unset($track['metadata']);
$track['variations'] = []; // No variations for public view
}
unset($track);
} else {
// For authenticated owner requests, convert URLs to proxy endpoint
foreach ($tracks as &$track) {
$track['audio_url'] = convertToProxyUrl($track['audio_url'], $track['task_id'] ?? '');
}
unset($track);
// Get variations for each track (only for owner)
$checkTable = $pdo->query("SHOW TABLES LIKE 'audio_variations'");
$hasVariationsTable = $checkTable->rowCount() > 0;
foreach ($tracks as &$track) {
$track['variations'] = [];
if ($hasVariationsTable) {
$varStmt = $pdo->prepare("
SELECT
variation_index,
audio_url,
duration,
title,
tags
FROM audio_variations
WHERE track_id = ?
ORDER BY variation_index ASC
");
$varStmt->execute([$track['id']]);
$variations = $varStmt->fetchAll(PDO::FETCH_ASSOC);
// Convert variation audio URLs to proxy format
foreach ($variations as &$var) {
$var['audio_url'] = convertToProxyUrl($var['audio_url'], $track['task_id'] ?? '');
}
unset($var);
$track['variations'] = $variations;
}
}
unset($track);
}
// Calculate total set duration
$total_set_duration = 0;
foreach ($tracks as $track) {
$duration = floatval($track['set_duration_minutes'] ?? 0);
$total_set_duration += $duration;
}
echo json_encode([
'success' => true,
'crate' => $crate,
'tracks' => $tracks,
'total_set_duration_minutes' => round($total_set_duration, 1),
'is_2_hour_set' => $total_set_duration >= 120,
'set_progress_percent' => min(100, round(($total_set_duration / 120) * 100, 1))
], JSON_PRETTY_PRINT);
} catch (Exception $e) {
error_log("Error getting crate tracks: " . $e->getMessage());
echo json_encode(['success' => false, 'error' => 'Failed to get crate tracks: ' . $e->getMessage()]);
}