![]() 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/ |
<?php
session_start();
require_once 'includes/translations.php';
require_once 'config/database.php';
if (!isset($_SESSION['user_id'])) {
header('Location: /auth/login.php');
exit;
}
$current_page = 'wishlist';
$page_title = t('wishlist.page_title');
$page_description = t('wishlist.page_description');
$pdo = getDBConnection();
try {
$pdo->exec("
CREATE TABLE IF NOT EXISTS user_wishlist (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
track_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_wishlist (user_id, track_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (track_id) REFERENCES music_tracks(id) ON DELETE CASCADE
)
");
} catch (Exception $e) {
error_log('Wishlist table ensure error: ' . $e->getMessage());
}
// OPTIMIZED: Using JOINs instead of correlated subqueries for better performance
$stmt = $pdo->prepare("
SELECT
mt.id,
mt.title,
mt.price,
mt.image_url,
mt.metadata,
mt.duration,
mt.audio_url,
mt.prompt,
mt.task_id,
u.name as artist_name,
u.id as artist_id,
uw.created_at as saved_at,
COALESCE(like_stats.like_count, 0) as like_count,
COALESCE(play_stats.play_count, 0) as play_count,
COALESCE(rating_stats.average_rating, 0) as average_rating,
COALESCE(rating_stats.rating_count, 0) as rating_count
FROM user_wishlist uw
INNER JOIN music_tracks mt ON uw.track_id = mt.id
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 play_count FROM track_plays GROUP BY track_id) play_stats ON mt.id = play_stats.track_id
LEFT JOIN (SELECT track_id, AVG(rating) as average_rating, COUNT(*) as rating_count FROM track_ratings GROUP BY track_id) rating_stats ON mt.id = rating_stats.track_id
WHERE uw.user_id = ?
ORDER BY uw.created_at DESC
");
$stmt->execute([$_SESSION['user_id']]);
$wishlist_tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
include 'includes/header.php';
function resolveTrackImage($track) {
$imageUrl = $track['image_url'] ?? null;
if ($imageUrl) {
$imageUrl = trim($imageUrl);
if ($imageUrl === '' || strtolower($imageUrl) === 'null') {
$imageUrl = null;
}
}
if ($imageUrl && !preg_match('/^https?:\/\//', $imageUrl)) {
return '/' . ltrim($imageUrl, '/');
}
if (!empty($track['metadata'])) {
$metadata = is_string($track['metadata']) ? json_decode($track['metadata'], true) : $track['metadata'];
if (isset($metadata['image_url']) && $metadata['image_url']) {
$metaUrl = $metadata['image_url'];
if (!preg_match('/^https?:\/\//', $metaUrl)) {
return '/' . ltrim($metaUrl, '/');
}
}
if (isset($metadata['cover_url']) && $metadata['cover_url']) {
$metaUrl = $metadata['cover_url'];
if (!preg_match('/^https?:\/\//', $metaUrl)) {
return '/' . ltrim($metaUrl, '/');
}
}
}
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';
}
?>
<div class="wishlist-page">
<div class="container">
<div class="wishlist-hero">
<div>
<p class="wishlist-badge"><i class="fas fa-heart"></i> <?= t('nav.wishlist') ?></p>
<h1><?= htmlspecialchars(t('wishlist.page_title')) ?></h1>
<p><?= htmlspecialchars(t('wishlist.page_description')) ?></p>
</div>
</div>
<?php if (empty($wishlist_tracks)): ?>
<div class="wishlist-empty" id="wishlistEmpty">
<i class="fas fa-heart-broken"></i>
<h2><?= t('wishlist.empty_title') ?></h2>
<p><?= t('wishlist.empty_desc') ?></p>
<a href="/community_fixed.php" class="btn-primary">
<i class="fas fa-compass"></i>
<?= t('community.hero_title') ?>
</a>
</div>
<?php else: ?>
<div class="wishlist-grid" id="wishlistItems">
<?php foreach ($wishlist_tracks as $track):
$imageUrl = resolveTrackImage($track);
$displayTitle = !empty($track['title']) ? $track['title'] : t('community.untitled_track');
$duration = $track['duration'] ? gmdate('i:s', $track['duration']) : '0:00';
$price = (!empty($track['price']) && floatval($track['price']) > 0) ? number_format(floatval($track['price']), 2) : '1.99';
$savedAt = date('M j, Y', strtotime($track['saved_at']));
?>
<div class="wishlist-item" data-track-id="<?= $track['id'] ?>">
<div class="wishlist-cover">
<img src="<?= htmlspecialchars($imageUrl) ?>" alt="<?= htmlspecialchars($displayTitle) ?>" loading="lazy">
</div>
<div class="wishlist-info">
<div class="wishlist-top-row">
<div>
<h3><?= htmlspecialchars($displayTitle) ?></h3>
<a href="/artist_profile.php?id=<?= $track['artist_id'] ?>" class="wishlist-artist">
<?= htmlspecialchars($track['artist_name']) ?>
</a>
</div>
<div class="wishlist-price">$<?= $price ?></div>
</div>
<div class="wishlist-meta">
<span><i class="fas fa-clock"></i> <?= $duration ?></span>
<span><i class="fas fa-play"></i> <?= number_format($track['play_count']) ?></span>
<span><i class="fas fa-heart"></i> <?= number_format($track['like_count']) ?></span>
<span><i class="fas fa-star"></i> <?= number_format($track['average_rating'], 1) ?>/10</span>
</div>
<div class="wishlist-actions">
<button class="btn-secondary" onclick="toggleWishlist(<?= $track['id'] ?>, this)" aria-pressed="true">
<i class="fas fa-times"></i>
<?= t('wishlist.remove') ?>
</button>
<a href="/track.php?id=<?= $track['id'] ?>" class="btn-ghost">
<i class="fas fa-external-link-alt"></i>
<?= t('wishlist.view_track') ?>
</a>
<button class="btn-primary" onclick="wishlistAddToCart(<?= $track['id'] ?>, '<?= htmlspecialchars($displayTitle, ENT_QUOTES) ?>', <?= $price ?>, this)">
<i class="fas fa-shopping-cart"></i>
<?= t('artist_profile.add_to_cart') ?>
</button>
</div>
<div class="wishlist-footer">
<span><i class="fas fa-bookmark"></i> <?= t('wishlist.added_on') ?> <?= $savedAt ?></span>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="wishlist-empty" id="wishlistEmpty" style="display:none;">
<i class="fas fa-heart-broken"></i>
<h2><?= t('wishlist.empty_title') ?></h2>
<p><?= t('wishlist.empty_desc') ?></p>
<a href="/community_fixed.php" class="btn-primary">
<i class="fas fa-compass"></i>
<?= t('community.hero_title') ?>
</a>
</div>
<?php endif; ?>
</div>
</div>
<style>
.wishlist-page {
background: linear-gradient(180deg, #05050b, #0f0f21 35%, #13132b);
min-height: 100vh;
padding: 80px 0 140px;
}
.wishlist-page .container {
max-width: 1100px;
margin: 0 auto;
padding: 0 24px;
}
.wishlist-hero {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40px;
color: white;
gap: 24px;
}
.wishlist-badge {
display: inline-flex;
align-items: center;
gap: 0.6rem;
background: rgba(255,255,255,0.08);
border: 1px solid rgba(255,255,255,0.15);
padding: 0.6rem 1.2rem;
border-radius: 999px;
font-size: 1.2rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.wishlist-hero h1 {
font-size: 3rem;
margin: 1rem 0 0.5rem;
}
.wishlist-hero p {
color: #cbd5ff;
max-width: 540px;
}
.wishlist-empty {
margin-top: 60px;
text-align: center;
color: white;
padding: 80px 20px;
border-radius: 24px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.wishlist-empty i {
font-size: 4rem;
margin-bottom: 20px;
}
.wishlist-grid {
display: flex;
flex-direction: column;
gap: 20px;
}
.wishlist-item {
display: grid;
grid-template-columns: minmax(180px, 240px) 1fr;
gap: 24px;
background: rgba(15, 15, 32, 0.9);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 24px;
padding: 24px;
align-items: center;
}
.wishlist-cover {
position: relative;
border-radius: 20px;
overflow: hidden;
}
.wishlist-cover img {
width: 100%;
height: clamp(200px, 26vw, 260px);
object-fit: cover;
display: block;
}
.wishlist-info h3 {
color: white;
margin: 0;
}
.wishlist-artist {
color: #a8b4ff;
text-decoration: none;
font-weight: 600;
}
.wishlist-meta {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
margin: 1rem 0;
color: #a0a0c0;
}
.wishlist-meta span {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.wishlist-actions {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.wishlist-top-row {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 16px;
}
.wishlist-top-row > div:first-child {
flex: 1;
min-width: 0;
}
.wishlist-price {
font-size: 1.8rem;
font-weight: 700;
color: white;
white-space: nowrap;
}
.btn-secondary, .btn-ghost, .btn-primary {
border-radius: 12px;
padding: 0.9rem 1.6rem;
border: none;
cursor: pointer;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 0.6rem;
text-decoration: none;
transition: all 0.3s ease;
}
.btn-secondary {
background: rgba(255, 255, 255, 0.08);
color: white;
border: 1px solid rgba(255, 255, 255, 0.15);
}
.btn-ghost {
background: transparent;
color: #cbd5ff;
border: 1px solid rgba(203, 213, 255, 0.3);
}
.btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
}
.wishlist-footer {
margin-top: 1rem;
color: #a0a0c0;
font-size: 0.95rem;
}
@media (max-width: 1024px) {
.wishlist-hero {
flex-direction: column;
align-items: flex-start;
text-align: left;
}
.wishlist-item {
grid-template-columns: 200px 1fr;
padding: 20px;
}
}
@media (max-width: 768px) {
.wishlist-page {
padding: 60px 0 100px;
}
.wishlist-hero h1 {
font-size: 2.4rem;
}
.wishlist-item {
grid-template-columns: 1fr;
}
.wishlist-cover img {
height: clamp(180px, 55vw, 240px);
}
.wishlist-top-row {
flex-direction: column;
align-items: flex-start;
}
.wishlist-price {
font-size: 1.5rem;
}
}
@media (max-width: 540px) {
.wishlist-page .container {
padding: 0 16px;
}
.wishlist-hero {
text-align: center;
align-items: center;
}
.wishlist-hero h1 {
font-size: 2rem;
}
.wishlist-meta {
flex-direction: column;
gap: 0.5rem;
}
.wishlist-actions {
flex-direction: column;
}
.wishlist-actions .btn-secondary,
.wishlist-actions .btn-ghost,
.wishlist-actions .btn-primary {
width: 100%;
justify-content: center;
}
}
</style>
<script>
function wishlistAddToCart(trackId, title, price, button) {
if (!trackId) return;
const originalHTML = button ? button.innerHTML : null;
if (button) {
button.disabled = true;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
}
const formData = new FormData();
formData.append('action', 'add');
formData.append('track_id', trackId);
formData.append('artist_plan', 'free');
fetch('/cart.php', { method: 'POST', body: formData })
.then(response => response.json())
.then(data => {
if (!data.success) throw new Error(data.message || 'Failed to add track to cart');
notifyWishlist(`"${title}" added to cart!`);
})
.catch(error => {
notifyWishlist(error.message || 'Unable to add to cart', 'error');
})
.finally(() => {
if (button && originalHTML) {
button.disabled = false;
button.innerHTML = originalHTML;
}
});
}
function notifyWishlist(message, type) {
if (typeof window.showNotification === 'function') {
window.showNotification(message, type || 'success');
} else {
alert(message);
}
}
function updateWishlistEmptyState() {
const container = document.getElementById('wishlistItems');
const emptyState = document.getElementById('wishlistEmpty');
if (!container || !emptyState) return;
const hasItems = container.querySelector('.wishlist-item') !== null;
emptyState.style.display = hasItems ? 'none' : 'flex';
}
window.addEventListener('wishlist:toggled', function(event) {
if (!event.detail || event.detail.added) return;
const item = document.querySelector(`.wishlist-item[data-track-id="${event.detail.trackId}"]`);
if (item) {
item.style.opacity = '0';
setTimeout(() => {
item.remove();
updateWishlistEmptyState();
}, 200);
}
});
document.addEventListener('DOMContentLoaded', updateWishlistEmptyState);
</script>
<?php include 'includes/footer.php'; ?>