![]() 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/.cursor-server/data/User/History/-24d6c3ee/ |
<?php
// Playlist Management Tab
// This file handles all playlist management functionality
// Ensure database connection is available
if (!isset($pdo)) {
echo '<div style="color: red; padding: 20px;">Database connection not available. Please access this page through the admin panel.</div>';
return;
}
// Playlist form submissions are now handled via AJAX in admin.php
// This prevents the fast refresh and bug issues
// Get playlist filter
$playlist_tab = $_GET['playlist_tab'] ?? 'all';
// Get tracks for playlist management
$playlist_where = "mt.status = 'complete' AND mt.audio_url IS NOT NULL";
$playlist_params = [];
if ($playlist_tab === 'featured') {
$playlist_where .= " AND mt.is_featured = 1";
} elseif ($playlist_tab === 'vip') {
$playlist_where .= " AND mt.is_vip_sample = 1";
}
$playlist_tracks = $pdo->prepare("
SELECT mt.*, u.name as artist_name,
(SELECT COUNT(*) FROM track_plays WHERE track_id = mt.id) as play_count,
(SELECT COUNT(*) FROM track_likes WHERE track_id = mt.id) as like_count
FROM music_tracks mt
JOIN users u ON mt.user_id = u.id
WHERE $playlist_where
ORDER BY mt.playlist_order ASC, mt.created_at DESC
LIMIT 100
");
$playlist_tracks->execute($playlist_params);
$tracks = $playlist_tracks->fetchAll();
// Debug: Check for duplicates
$track_ids = array_column($tracks, 'id');
$duplicate_ids = array_diff_assoc($track_ids, array_unique($track_ids));
if (!empty($duplicate_ids)) {
error_log("Duplicate track IDs found in admin playlists: " . implode(', ', array_unique($duplicate_ids)));
}
// Get playlist stats
$playlist_stats = [
'total' => $pdo->query("SELECT COUNT(*) FROM music_tracks mt WHERE mt.status = 'complete' AND mt.audio_url IS NOT NULL")->fetchColumn(),
'featured' => $pdo->query("SELECT COUNT(*) FROM music_tracks mt WHERE mt.status = 'complete' AND mt.audio_url IS NOT NULL AND mt.is_featured = 1")->fetchColumn(),
'vip' => $pdo->query("SELECT COUNT(*) FROM music_tracks mt WHERE mt.status = 'complete' AND mt.audio_url IS NOT NULL AND mt.is_vip_sample = 1")->fetchColumn()
];
?>
<!-- Playlist Management -->
<div class="section-header">
<h2><i class="fas fa-list-music"></i> Playlist Management</h2>
<p>Manage Featured tracks and VIP Sample playlists for the homepage global player</p>
</div>
<style>
.playlist-filters {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.filter-btn {
padding: 8px 16px;
border: 1px solid #d1d5db;
background: white;
color: #374151;
text-decoration: none;
border-radius: 6px;
transition: all 0.2s ease;
}
.filter-btn:hover {
background: #f9fafb;
border-color: #9ca3af;
}
.filter-btn.active {
background: #3b82f6;
border-color: #3b82f6;
color: white;
}
.track-grid {
display: grid;
gap: 15px;
margin-top: 20px;
}
.track-item {
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 15px;
display: flex;
align-items: center;
gap: 15px;
transition: all 0.2s ease;
}
.track-item:hover {
border-color: #9ca3af;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.track-info {
flex: 1;
}
.track-title {
font-weight: 600;
margin-bottom: 4px;
}
.track-artist {
color: #6b7280;
font-size: 0.9em;
margin-bottom: 4px;
}
.track-stats {
color: #9ca3af;
font-size: 0.8em;
}
.track-controls {
display: flex;
align-items: center;
gap: 15px;
}
.play-btn {
background: #3b82f6;
color: white;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.play-btn:hover {
background: #2563eb;
transform: scale(1.05);
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.checkbox-group label {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.9em;
cursor: pointer;
}
.order-input {
width: 60px;
padding: 4px 8px;
border: 1px solid #d1d5db;
border-radius: 4px;
text-align: center;
}
/* AJAX update feedback styles */
.checkbox-group, .track-controls > div {
position: relative;
}
.update-feedback {
position: absolute;
font-size: 12px;
margin-top: 2px;
font-weight: 500;
transition: opacity 0.3s ease;
}
/* Loading state styles */
.checkbox-group input:disabled,
.order-input:disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* Success/error states */
.checkbox-group input:disabled + span,
.order-input:disabled {
opacity: 0.7;
}
</style>
<!-- Playlist Statistics -->
<div class="stats-grid" style="margin-bottom: 2rem;">
<div class="stat-card">
<div class="stat-number"><?= number_format($playlist_stats['total']) ?></div>
<div class="stat-label">Total Tracks</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= number_format($playlist_stats['featured']) ?></div>
<div class="stat-label">Featured</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= number_format($playlist_stats['vip']) ?></div>
<div class="stat-label">VIP Samples</div>
</div>
</div>
<!-- Playlist Filters -->
<div class="playlist-filters">
<a href="?tab=playlists&playlist_tab=all" class="filter-btn <?= $playlist_tab === 'all' ? 'active' : '' ?>">
<i class="fas fa-list"></i> All Tracks (<?= number_format($playlist_stats['total']) ?>)
</a>
<a href="?tab=playlists&playlist_tab=featured" class="filter-btn <?= $playlist_tab === 'featured' ? 'active' : '' ?>">
<i class="fas fa-star"></i> Featured (<?= number_format($playlist_stats['featured']) ?>)
</a>
<a href="?tab=playlists&playlist_tab=vip" class="filter-btn <?= $playlist_tab === 'vip' ? 'active' : '' ?>">
<i class="fas fa-crown"></i> VIP Samples (<?= number_format($playlist_stats['vip']) ?>)
</a>
</div>
<!-- Track Grid -->
<div class="track-grid">
<?php if (empty($tracks)): ?>
<div style="text-align: center; padding: 40px; color: #6b7280;">
<i class="fas fa-music" style="font-size: 48px; margin-bottom: 16px; opacity: 0.5;"></i>
<h3>No tracks found</h3>
<p>No tracks match the current filter criteria.</p>
</div>
<?php else: ?>
<?php foreach ($tracks as $track): ?>
<div class="track-item">
<button class="play-btn" onclick="playTrack('<?= htmlspecialchars($track['audio_url']) ?>', '<?= htmlspecialchars($track['title']) ?>', '<?= htmlspecialchars($track['artist_name']) ?>')">
<i class="fas fa-play"></i>
</button>
<div class="track-info">
<div class="track-title"><?= htmlspecialchars($track['title']) ?></div>
<div class="track-artist">by <?= htmlspecialchars($track['artist_name']) ?></div>
<div class="track-stats"><?= $track['play_count'] ?> plays • <?= $track['like_count'] ?> likes</div>
</div>
<div class="track-controls">
<!-- Featured Toggle -->
<div class="checkbox-group">
<label>
<input type="checkbox"
<?= $track['is_featured'] ? 'checked' : '' ?>
onchange="updatePlaylistSetting(<?= $track['id'] ?>, 'toggle_featured', this.checked, 'is_featured')">
<span style="color: #f59e0b;"><i class="fas fa-star"></i> Featured</span>
</label>
</div>
<!-- VIP Toggle -->
<div class="checkbox-group">
<label>
<input type="checkbox"
<?= $track['is_vip_sample'] ? 'checked' : '' ?>
onchange="updatePlaylistSetting(<?= $track['id'] ?>, 'toggle_vip', this.checked, 'is_vip')">
<span style="color: #8b5cf6;"><i class="fas fa-crown"></i> VIP</span>
</label>
</div>
<!-- Order Input -->
<div style="text-align: center;">
<label style="font-size: 0.8em; color: #6b7280; display: block; margin-bottom: 4px;">Order</label>
<input type="number"
value="<?= $track['playlist_order'] ?>"
class="order-input" min="0" max="999"
onchange="updatePlaylistOrder(<?= $track['id'] ?>, this.value)"
onblur="updatePlaylistOrder(<?= $track['id'] ?>, this.value)">
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<script>
// Play track function for playlist management
function playTrack(audioUrl, title, artist) {
if (typeof window.playTrack === 'function') {
window.playTrack(audioUrl, title, artist);
} else if (typeof window.enhancedGlobalPlayer !== 'undefined' && typeof window.enhancedGlobalPlayer.playTrack === 'function') {
window.enhancedGlobalPlayer.playTrack(audioUrl, title, artist);
} else {
console.log('Playing:', title, 'by', artist);
// Fallback to basic audio
const audio = new Audio(audioUrl);
audio.volume = 0.5;
audio.play().catch(error => console.error('Audio play failed:', error));
}
}
// AJAX playlist update functions
function updatePlaylistSetting(trackId, action, value, settingType) {
console.log('Updating playlist setting:', { trackId, action, value, settingType });
// Show loading state
const checkbox = event.target;
const originalChecked = checkbox.checked;
checkbox.disabled = true;
// Prepare form data
const formData = new FormData();
formData.append('playlist_action', action);
formData.append('track_id', trackId);
formData.append('playlist_tab', '<?= $playlist_tab ?>');
if (action === 'toggle_featured') {
formData.append('is_featured', value ? '1' : '0');
} else if (action === 'toggle_vip') {
formData.append('is_vip', value ? '1' : '0');
}
// Send AJAX request
fetch('admin.php?tab=playlists&ajax=1', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
console.log('Playlist setting updated successfully');
// Show success feedback
showPlaylistUpdateFeedback(checkbox, true);
} else {
console.error('Failed to update playlist setting:', data.error);
// Revert checkbox state
checkbox.checked = !originalChecked;
showPlaylistUpdateFeedback(checkbox, false);
}
})
.catch(error => {
console.error('Error updating playlist setting:', error);
// Revert checkbox state
checkbox.checked = !originalChecked;
showPlaylistUpdateFeedback(checkbox, false);
})
.finally(() => {
checkbox.disabled = false;
});
}
// Debounce function to prevent rapid AJAX calls
let updateOrderTimeout = null;
function updatePlaylistOrder(trackId, order) {
console.log('Updating playlist order:', { trackId, order });
// Validate order value
const orderNum = parseInt(order);
if (isNaN(orderNum) || orderNum < 0 || orderNum > 999) {
console.error('Invalid order value:', order);
return;
}
// Clear previous timeout
if (updateOrderTimeout) {
clearTimeout(updateOrderTimeout);
}
// Debounce the update
updateOrderTimeout = setTimeout(() => {
// Show loading state
const input = event.target;
const originalValue = input.value;
input.disabled = true;
// Prepare form data
const formData = new FormData();
formData.append('playlist_action', 'update_order');
formData.append('track_id', trackId);
formData.append('order', orderNum);
formData.append('playlist_tab', '<?= $playlist_tab ?>');
// Send AJAX request
fetch('admin.php?tab=playlists&ajax=1', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
console.log('Playlist order updated successfully');
showPlaylistUpdateFeedback(input, true);
} else {
console.error('Failed to update playlist order:', data.error);
input.value = originalValue;
showPlaylistUpdateFeedback(input, false);
}
})
.catch(error => {
console.error('Error updating playlist order:', error);
input.value = originalValue;
showPlaylistUpdateFeedback(input, false);
})
.finally(() => {
input.disabled = false;
});
}, 500); // 500ms debounce
function showPlaylistUpdateFeedback(element, success) {
// Create or update feedback element
let feedback = element.parentNode.querySelector('.update-feedback');
if (!feedback) {
feedback = document.createElement('div');
feedback.className = 'update-feedback';
feedback.style.cssText = 'position: absolute; font-size: 12px; margin-top: 2px;';
element.parentNode.style.position = 'relative';
element.parentNode.appendChild(feedback);
}
if (success) {
feedback.textContent = '✓ Updated';
feedback.style.color = '#10b981';
} else {
feedback.textContent = '✗ Failed';
feedback.style.color = '#ef4444';
}
// Clear feedback after 2 seconds
setTimeout(() => {
if (feedback && feedback.parentNode) {
feedback.remove();
}
}, 2000);
}
</script>