![]() 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
session_start();
// Include translation system
require_once 'includes/translations.php';
require_once 'config/database.php';
// Set page variables for header
$current_page = 'charts';
$page_title = t('charts.page_title');
$page_description = t('charts.page_description');
include 'includes/header.php';
$pdo = getDBConnection();
$user_id = $_SESSION['user_id'] ?? null;
// Get chart data
try {
// Top Tracks by Likes - Using JOINs for better performance
$top_liked_query = "
SELECT
mt.id,
mt.task_id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.metadata,
mt.genre as track_genre,
mt.user_id,
mt.variations_count,
u.name as artist_name,
u.profile_image,
COALESCE(COUNT(DISTINCT tl.id), 0) as like_count,
COALESCE(COUNT(DISTINCT tp.id), 0) as play_count,
COALESCE(COUNT(DISTINCT tc.id), 0) as comment_count,
COALESCE(COUNT(DISTINCT ts.id), 0) as share_count,
COALESCE(COUNT(DISTINCT tv.id), 0) as view_count
FROM music_tracks mt
JOIN users u ON mt.user_id = u.id
LEFT JOIN track_likes tl ON mt.id = tl.track_id
LEFT JOIN track_plays tp ON mt.id = tp.track_id
LEFT JOIN track_comments tc ON mt.id = tc.track_id
LEFT JOIN track_shares ts ON mt.id = ts.track_id
LEFT JOIN track_views tv ON mt.id = tv.track_id
WHERE mt.status = 'complete'
AND (mt.audio_url IS NOT NULL AND mt.audio_url != '' OR mt.variations_count > 0)
AND (mt.is_public = 1 OR mt.is_public IS NULL)
GROUP BY mt.id, mt.task_id, mt.title, mt.audio_url, mt.duration, mt.created_at, mt.metadata, mt.genre, mt.user_id, mt.variations_count, u.name, u.profile_image
ORDER BY like_count DESC, play_count DESC
LIMIT 20
";
$stmt = $pdo->prepare($top_liked_query);
$stmt->execute();
$top_liked_tracks = $stmt->fetchAll();
// Top Tracks by Plays - Using JOINs for better performance
$top_played_query = "
SELECT
mt.id,
mt.task_id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.metadata,
mt.genre as track_genre,
mt.user_id,
mt.variations_count,
u.name as artist_name,
u.profile_image,
COALESCE(COUNT(DISTINCT tl.id), 0) as like_count,
COALESCE(COUNT(DISTINCT tp.id), 0) as play_count,
COALESCE(COUNT(DISTINCT tc.id), 0) as comment_count,
COALESCE(COUNT(DISTINCT ts.id), 0) as share_count,
COALESCE(COUNT(DISTINCT tv.id), 0) as view_count
FROM music_tracks mt
JOIN users u ON mt.user_id = u.id
LEFT JOIN track_likes tl ON mt.id = tl.track_id
LEFT JOIN track_plays tp ON mt.id = tp.track_id
LEFT JOIN track_comments tc ON mt.id = tc.track_id
LEFT JOIN track_shares ts ON mt.id = ts.track_id
LEFT JOIN track_views tv ON mt.id = tv.track_id
WHERE mt.status = 'complete'
AND (mt.audio_url IS NOT NULL AND mt.audio_url != '' OR mt.variations_count > 0)
AND (mt.is_public = 1 OR mt.is_public IS NULL)
GROUP BY mt.id, mt.task_id, mt.title, mt.audio_url, mt.duration, mt.created_at, mt.metadata, mt.genre, mt.user_id, mt.variations_count, u.name, u.profile_image
ORDER BY play_count DESC, like_count DESC
LIMIT 20
";
$stmt = $pdo->prepare($top_played_query);
$stmt->execute();
$top_played_tracks = $stmt->fetchAll();
// Trending Genres - Optimized with aggregated JOINs instead of correlated subqueries
$trending_genres_query = "
SELECT
COALESCE(
NULLIF(mt.genre, ''),
JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.genre')),
'Unknown'
) as genre,
COUNT(DISTINCT mt.id) as track_count,
COALESCE(SUM(like_stats.like_count), 0) as total_likes,
COALESCE(SUM(play_stats.play_count), 0) as total_plays,
COALESCE(SUM(comment_stats.comment_count), 0) as total_comments
FROM music_tracks mt
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, 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 != '' OR mt.variations_count > 0)
AND (mt.is_public = 1 OR mt.is_public IS NULL)
AND (
(mt.genre IS NOT NULL AND mt.genre != '')
OR JSON_EXTRACT(mt.metadata, '$.genre') IS NOT NULL
)
GROUP BY COALESCE(
NULLIF(mt.genre, ''),
JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.genre')),
'Unknown'
)
HAVING genre != 'Unknown' AND genre IS NOT NULL AND genre != ''
ORDER BY total_likes DESC, total_plays DESC
LIMIT 10
";
$stmt = $pdo->prepare($trending_genres_query);
$stmt->execute();
$trending_genres = $stmt->fetchAll();
// Top Artists - Optimized with aggregated JOINs instead of correlated subqueries
// Show all artists who have at least one complete, public track with audio
$top_artists_query = "
SELECT
u.id,
u.name as artist_name,
u.profile_image,
COUNT(DISTINCT mt.id) as track_count,
COALESCE(SUM(like_stats.like_count), 0) as total_likes,
COALESCE(SUM(play_stats.play_count), 0) as total_plays,
COALESCE(SUM(comment_stats.comment_count), 0) as total_comments
FROM users u
JOIN music_tracks mt ON u.id = mt.user_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, 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 != '' OR mt.variations_count > 0)
AND (mt.is_public = 1 OR mt.is_public IS NULL)
GROUP BY u.id, u.name, u.profile_image
HAVING track_count > 0
ORDER BY total_likes DESC, total_plays DESC, track_count DESC
";
$stmt = $pdo->prepare($top_artists_query);
$stmt->execute();
$top_artists = $stmt->fetchAll();
// Trending Tracks - Show most rated tracks ordered by average rating and number of ratings
$recent_hot_query = "
SELECT
mt.id,
mt.task_id,
mt.title,
mt.audio_url,
mt.duration,
mt.created_at,
mt.metadata,
mt.genre as track_genre,
mt.user_id,
mt.variations_count,
u.name as artist_name,
u.profile_image,
COALESCE(AVG(tr.rating), 0) as average_rating,
COALESCE(COUNT(DISTINCT tr.id), 0) as rating_count,
COALESCE(COUNT(DISTINCT tl.id), 0) as total_likes,
COALESCE(COUNT(DISTINCT tp.id), 0) as total_plays,
COALESCE(COUNT(DISTINCT tc.id), 0) as total_comments
FROM music_tracks mt
JOIN users u ON mt.user_id = u.id
LEFT JOIN track_ratings tr ON mt.id = tr.track_id
LEFT JOIN track_likes tl ON mt.id = tl.track_id
LEFT JOIN track_plays tp ON mt.id = tp.track_id
LEFT JOIN track_comments tc ON mt.id = tc.track_id
WHERE mt.status = 'complete'
AND (mt.audio_url IS NOT NULL AND mt.audio_url != '' OR mt.variations_count > 0)
AND (mt.is_public = 1 OR mt.is_public IS NULL)
GROUP BY mt.id, mt.task_id, mt.title, mt.audio_url, mt.duration, mt.created_at, mt.metadata, mt.genre, mt.user_id, mt.variations_count, u.name, u.profile_image
HAVING rating_count > 0
ORDER BY average_rating DESC, rating_count DESC, total_likes DESC, total_plays DESC
LIMIT 20
";
$stmt = $pdo->prepare($recent_hot_query);
$stmt->execute();
$recent_hot_tracks = $stmt->fetchAll();
} catch (Exception $e) {
error_log("Charts error: " . $e->getMessage());
error_log("Charts error trace: " . $e->getTraceAsString());
// Initialize empty arrays if query fails
if (!isset($top_liked_tracks)) $top_liked_tracks = [];
if (!isset($top_played_tracks)) $top_played_tracks = [];
if (!isset($trending_genres)) $trending_genres = [];
if (!isset($top_artists)) $top_artists = [];
if (!isset($recent_hot_tracks)) $recent_hot_tracks = [];
}
// Process tracks to use selected variation audio_url and duration if available
function processTrackVariations(&$tracks, $pdo) {
foreach ($tracks as &$track) {
// Default to original track duration
$track['preferred_duration'] = $track['duration'];
$track['selected_variation_index'] = null; // Track which variation is selected for proxy URL
// CRITICAL: Always check for selected_variation FIRST (even if main audio_url exists)
// This ensures the user's preferred variation is used, not the default track
$trackMetadata = json_decode($track['metadata'] ?? '{}', true) ?: [];
$selectedVariationIndex = $trackMetadata['selected_variation'] ?? null;
// Also check selected_variation column if it exists in the track data
if ($selectedVariationIndex === null && isset($track['selected_variation']) && $track['selected_variation'] !== null) {
$selectedVariationIndex = (int)$track['selected_variation'];
}
if ($selectedVariationIndex !== null) {
// Get the selected variation from audio_variations table (including duration)
$stmt = $pdo->prepare("SELECT audio_url, duration FROM audio_variations WHERE track_id = ? AND variation_index = ?");
$stmt->execute([$track['id'], $selectedVariationIndex]);
$variation = $stmt->fetch();
if ($variation && $variation['audio_url']) {
// Store the variation index so we can use play_audio.php with variation parameter
$track['selected_variation_index'] = $selectedVariationIndex;
// Use play_audio.php proxy for variations (like community_fixed.php does)
// This ensures the variation is properly served
$track['preferred_audio_url'] = '/utils/play_audio.php?id=' . $track['id'] . '&variation=' . $selectedVariationIndex;
// Use variation duration if available, otherwise keep main track duration
if (!empty($variation['duration'])) {
$track['preferred_duration'] = $variation['duration'];
}
} else {
// Selected variation not found, fall back to main track
$track['selected_variation_index'] = null;
if (empty($track['audio_url']) && !empty($track['task_id'])) {
$track['preferred_audio_url'] = '/utils/audiofiles.php?id=' . urlencode($track['task_id']);
} else {
$track['preferred_audio_url'] = $track['audio_url'] ?? '';
}
}
} else {
// No selected variation - use main track or best variation if main is empty
$track['selected_variation_index'] = null;
if (empty($track['audio_url']) && !empty($track['task_id'])) {
$track['preferred_audio_url'] = '/utils/audiofiles.php?id=' . urlencode($track['task_id']);
} else {
$track['preferred_audio_url'] = $track['audio_url'] ?? '';
}
// If main audio_url is still empty, try to find best variation (longest duration)
if (empty($track['preferred_audio_url']) && !empty($track['variations_count']) && $track['variations_count'] > 0) {
// Get all variations and prefer the one with the longest duration (likely the full track, not a preview)
$stmt = $pdo->prepare("SELECT variation_index, audio_url, duration FROM audio_variations WHERE track_id = ? ORDER BY duration DESC, variation_index ASC LIMIT 1");
$stmt->execute([$track['id']]);
$bestVariation = $stmt->fetch();
if ($bestVariation && $bestVariation['audio_url']) {
// Use play_audio.php proxy for the best variation
$track['selected_variation_index'] = $bestVariation['variation_index'];
$track['preferred_audio_url'] = '/utils/play_audio.php?id=' . $track['id'] . '&variation=' . $bestVariation['variation_index'];
if (!empty($bestVariation['duration'])) {
$track['preferred_duration'] = $bestVariation['duration'];
}
} elseif (!empty($track['task_id'])) {
// Fallback to proxy endpoint if no variations found
$track['preferred_audio_url'] = '/utils/audiofiles.php?id=' . urlencode($track['task_id']);
}
} elseif (empty($track['preferred_audio_url']) && !empty($track['task_id'])) {
// No variations, use proxy endpoint
$track['preferred_audio_url'] = '/utils/audiofiles.php?id=' . urlencode($track['task_id']);
}
}
}
}
// Apply variation processing to all track arrays
if (isset($top_liked_tracks) && !empty($top_liked_tracks)) {
processTrackVariations($top_liked_tracks, $pdo);
}
if (isset($top_played_tracks) && !empty($top_played_tracks)) {
processTrackVariations($top_played_tracks, $pdo);
}
if (isset($recent_hot_tracks) && !empty($recent_hot_tracks)) {
processTrackVariations($recent_hot_tracks, $pdo);
}
// Helper function to format duration
function formatDuration($seconds) {
$minutes = floor($seconds / 60);
$seconds = $seconds % 60;
return sprintf('%d:%02d', $minutes, $seconds);
}
// Helper function to get artist initial
function getArtistInitial($name) {
return strtoupper(substr($name, 0, 1));
}
// Helper function to parse metadata
function parseMetadata($metadata, $track_genre = null) {
$data = json_decode($metadata ?? '{}', true) ?: [];
return [
'genre' => $track_genre ?? $data['genre'] ?? 'Unknown',
'bpm' => $data['bpm'] ?? '120',
'key' => $data['key'] ?? 'C major',
'mood' => $data['mood'] ?? 'Neutral',
'energy' => $data['energy'] ?? 'Medium'
];
}
// Helper function to convert external audio URLs to proxy endpoint
function getPlayableAudioUrl($audioUrl, $taskId) {
// If URL is already a local path or proxy endpoint, return as is
if (empty($audioUrl)) {
// If empty but we have task_id, use proxy endpoint
if (!empty($taskId)) {
return '/utils/audiofiles.php?id=' . urlencode($taskId);
}
return '';
}
// If it's already a proxy endpoint (audiofiles.php or play_audio.php), return as-is
if (strpos($audioUrl, '/utils/audiofiles') === 0 ||
strpos($audioUrl, '/audiofiles') === 0 ||
strpos($audioUrl, '/utils/play_audio') === 0 ||
strpos($audioUrl, '/play_audio') === 0) {
return $audioUrl;
}
// If it's a local file path, check if file exists and is not empty
if (strpos($audioUrl, '/audio_files/') === 0 ||
strpos($audioUrl, '/uploads/') === 0 ||
(strpos($audioUrl, '/') === 0 && strpos($audioUrl, 'http') !== 0 && strpos($audioUrl, '/utils/') !== 0)) {
// It's a local path - check if file exists and is not empty
$localPath = __DIR__ . $audioUrl;
if (file_exists($localPath) && filesize($localPath) > 0) {
// File exists and has content - return as-is
return $audioUrl;
} else {
// File is empty or doesn't exist - try to use proxy endpoint if we have task_id
if (!empty($taskId)) {
return '/utils/audiofiles.php?id=' . urlencode($taskId);
}
// No task_id available - return original (might be external URL)
return $audioUrl;
}
}
// If it's an external URL (API.box, http/https, cdn1.suno.ai, etc.), convert to proxy endpoint
if (strpos($audioUrl, 'http') === 0 ||
strpos($audioUrl, 'api.box') !== false ||
strpos($audioUrl, 'apiboxfiles.erweima.ai') !== false ||
strpos($audioUrl, 'cdn1.suno.ai') !== false ||
strpos($audioUrl, 'cdn') !== false) {
// Use the proxy endpoint with task_id
if (!empty($taskId)) {
return '/utils/audiofiles.php?id=' . urlencode($taskId);
}
}
// Fallback: return original URL
return $audioUrl;
}
?>
<div class="charts-container">
<!-- Hero Section -->
<section class="charts-hero">
<div class="hero-content">
<h1 class="hero-title">
<i class="fas fa-chart-line"></i>
<?= t('charts.hero_title') ?>
</h1>
<p class="hero-subtitle"><?= t('charts.hero_subtitle') ?></p>
<?php
// Get total stats for hero section - Optimized single query
try {
$hero_stats_stmt = $pdo->prepare("
SELECT
COUNT(*) as total_tracks,
COUNT(DISTINCT COALESCE(
NULLIF(genre, ''),
JSON_UNQUOTE(JSON_EXTRACT(metadata, '$.genre'))
)) as total_genres,
COUNT(DISTINCT user_id) as total_artists
FROM music_tracks
WHERE status = 'complete'
AND (audio_url IS NOT NULL AND audio_url != '' OR variations_count > 0)
AND (is_public = 1 OR is_public IS NULL)
AND (
(genre IS NOT NULL AND genre != '')
OR JSON_EXTRACT(metadata, '$.genre') IS NOT NULL
OR user_id IS NOT NULL
)
");
$hero_stats_stmt->execute();
$hero_stats = $hero_stats_stmt->fetch();
$total_tracks = $hero_stats['total_tracks'] ?? 0;
$total_genres = $hero_stats['total_genres'] ?? 0;
$total_artists = $hero_stats['total_artists'] ?? 0;
} catch (Exception $e) {
$total_tracks = count($recent_hot_tracks);
$total_genres = count($trending_genres);
$total_artists = count($top_artists);
}
?>
<div class="hero-stats">
<div class="stat-item">
<div class="stat-number"><?= number_format($total_tracks) ?></div>
<div class="stat-label"><?= t('charts.trending_tracks') ?></div>
</div>
<div class="stat-item">
<div class="stat-number"><?= number_format($total_genres) ?></div>
<div class="stat-label"><?= t('charts.hot_genres') ?></div>
</div>
<div class="stat-item">
<div class="stat-number"><?= number_format($total_artists) ?></div>
<div class="stat-label"><?= t('charts.top_artists') ?></div>
</div>
</div>
</div>
</section>
<!-- Chart Navigation -->
<section class="chart-nav">
<div class="nav-tabs">
<button class="nav-tab" onclick="showChart('trending')">
<i class="fas fa-fire"></i>
<?= t('charts.trending') ?>
</button>
<button class="nav-tab" onclick="showChart('most-liked')">
<i class="fas fa-heart"></i>
<?= t('charts.most_liked') ?>
</button>
<button class="nav-tab active" onclick="showChart('most-played')">
<i class="fas fa-play"></i>
<?= t('charts.most_played') ?>
</button>
<button class="nav-tab" onclick="showChart('genres')">
<i class="fas fa-music"></i>
<?= t('charts.genres') ?>
</button>
<button class="nav-tab" onclick="showChart('artists')">
<i class="fas fa-users"></i>
<?= t('charts.artists') ?>
</button>
</div>
</section>
<!-- Trending Chart -->
<section id="trending-chart" class="chart-section">
<div class="chart-header">
<h2><i class="fas fa-fire"></i> <?= t('charts.trending_now') ?></h2>
<p><?= t('charts.trending_description') ?></p>
</div>
<div class="tracks-grid">
<?php if (empty($recent_hot_tracks)): ?>
<div style="grid-column: 1 / -1; text-align: center; padding: 4rem 2rem; color: #a0aec0;">
<div style="font-size: 4rem; margin-bottom: 1rem; opacity: 0.5;">⭐</div>
<h3 style="font-size: 2rem; color: white; margin-bottom: 1rem;"><?= t('charts.no_rated_tracks') ?></h3>
<p style="font-size: 1.2rem;"><?= t('charts.start_rating_tracks') ?></p>
</div>
<?php else: ?>
<?php foreach ($recent_hot_tracks as $index => $track):
$metadata = parseMetadata($track['metadata'], $track['track_genre'] ?? null);
$artist_initial = getArtistInitial($track['artist_name']);
// First track plays the entire playlist, others play individual tracks
$playFunction = $index === 0 ? 'playChartsPlaylist()' : "playTrack('" . htmlspecialchars(getPlayableAudioUrl($track['preferred_audio_url'] ?? $track['audio_url'], $track['task_id'] ?? '')) . "', '" . htmlspecialchars($track['title']) . "', '" . htmlspecialchars($track['artist_name']) . "')";
?>
<div class="track-card chart-card" data-track-id="<?= $track['id'] ?>">
<div class="chart-position">#<?= $index + 1 ?></div>
<div class="track-header">
<div class="artist-avatar">
<span class="artist-initial"><?= $artist_initial ?></span>
</div>
<div class="track-info">
<h3 class="track-title">
<a href="/track.php?id=<?= $track['id'] ?>" class="track-title-link"><?= htmlspecialchars($track['title']) ?></a>
</h3>
<p class="track-artist"><?= t('charts.by') ?> <a href="/artist_profile.php?id=<?= $track['user_id'] ?>" class="artist-name-link"><?= htmlspecialchars($track['artist_name']) ?></a></p>
</div>
<div class="track-actions">
<button class="play-btn" onclick="<?= $playFunction ?>" title="<?= $index === 0 ? t('charts.play_chart_playlist') : t('charts.play') ?>">
<i class="fas fa-play"></i>
</button>
</div>
</div>
<div class="track-stats">
<div class="stat-item">
<i class="fas fa-star"></i>
<span><?= number_format($track['average_rating'], 1) ?>/10</span>
</div>
<div class="stat-item">
<i class="fas fa-users"></i>
<span><?= number_format($track['rating_count']) ?> <?= t('charts.ratings') ?></span>
</div>
<div class="stat-item">
<i class="fas fa-heart"></i>
<span><?= number_format($track['total_likes']) ?></span>
</div>
<div class="stat-item">
<i class="fas fa-play"></i>
<span><?= number_format($track['total_plays']) ?></span>
</div>
</div>
<div class="track-genre">
<span class="genre-tag"><?= htmlspecialchars($metadata['genre']) ?></span>
</div>
<div class="track-duration">
<?= formatDuration($track['preferred_duration'] ?? $track['duration']) ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</section>
<!-- Most Liked Chart -->
<section id="most-liked-chart" class="chart-section">
<div class="chart-header">
<h2><i class="fas fa-heart"></i> <?= t('charts.most_liked') ?></h2>
<p><?= t('charts.most_liked_description') ?></p>
</div>
<div class="tracks-grid">
<?php if (empty($top_liked_tracks)): ?>
<div style="grid-column: 1 / -1; text-align: center; padding: 4rem 2rem; color: #a0aec0;">
<div style="font-size: 4rem; margin-bottom: 1rem; opacity: 0.5;">❤️</div>
<h3 style="font-size: 2rem; color: white; margin-bottom: 1rem;"><?= t('charts.no_liked_tracks') ?></h3>
<p style="font-size: 1.2rem;"><?= t('charts.start_liking_tracks') ?></p>
</div>
<?php else: ?>
<?php foreach ($top_liked_tracks as $index => $track):
$metadata = parseMetadata($track['metadata'], $track['track_genre'] ?? null);
$artist_initial = getArtistInitial($track['artist_name']);
// First track plays the entire playlist, others play individual tracks
$playFunction = $index === 0 ? 'playChartsPlaylist()' : "playTrack('" . htmlspecialchars(getPlayableAudioUrl($track['preferred_audio_url'] ?? $track['audio_url'], $track['task_id'] ?? '')) . "', '" . htmlspecialchars($track['title']) . "', '" . htmlspecialchars($track['artist_name']) . "')";
?>
<div class="track-card chart-card" data-track-id="<?= $track['id'] ?>">
<div class="chart-position">#<?= $index + 1 ?></div>
<div class="track-header">
<div class="artist-avatar">
<span class="artist-initial"><?= $artist_initial ?></span>
</div>
<div class="track-info">
<h3 class="track-title">
<a href="/track.php?id=<?= $track['id'] ?>" class="track-title-link"><?= htmlspecialchars($track['title']) ?></a>
</h3>
<p class="track-artist"><?= t('charts.by') ?> <a href="/artist_profile.php?id=<?= $track['user_id'] ?>" class="artist-name-link"><?= htmlspecialchars($track['artist_name']) ?></a></p>
</div>
<div class="track-actions">
<button class="play-btn" onclick="<?= $playFunction ?>" title="<?= $index === 0 ? t('charts.play_chart_playlist') : t('charts.play') ?>">
<i class="fas fa-play"></i>
</button>
</div>
</div>
<div class="track-stats">
<div class="stat-item">
<i class="fas fa-heart"></i>
<span><?= number_format($track['like_count']) ?></span>
</div>
<div class="stat-item">
<i class="fas fa-play"></i>
<span><?= number_format($track['play_count']) ?></span>
</div>
<div class="stat-item">
<i class="fas fa-comment"></i>
<span><?= number_format($track['comment_count']) ?></span>
</div>
</div>
<div class="track-genre">
<span class="genre-tag"><?= htmlspecialchars($metadata['genre']) ?></span>
</div>
<div class="track-duration">
<?= formatDuration($track['preferred_duration'] ?? $track['duration']) ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</section>
<!-- Most Played Chart -->
<section id="most-played-chart" class="chart-section active">
<div class="chart-header">
<h2><i class="fas fa-play"></i> <?= t('charts.most_played') ?></h2>
<p><?= t('charts.most_played_description') ?></p>
</div>
<div class="tracks-grid">
<?php if (empty($top_played_tracks)): ?>
<div style="grid-column: 1 / -1; text-align: center; padding: 4rem 2rem; color: #a0aec0;">
<div style="font-size: 4rem; margin-bottom: 1rem; opacity: 0.5;">▶️</div>
<h3 style="font-size: 2rem; color: white; margin-bottom: 1rem;"><?= t('charts.no_played_tracks') ?></h3>
<p style="font-size: 1.2rem;"><?= t('charts.start_playing_tracks') ?></p>
</div>
<?php else: ?>
<?php foreach ($top_played_tracks as $index => $track):
$metadata = parseMetadata($track['metadata'], $track['track_genre'] ?? null);
$artist_initial = getArtistInitial($track['artist_name']);
// First track plays the entire playlist, others play individual tracks
$playFunction = $index === 0 ? 'playChartsPlaylist()' : "playTrack('" . htmlspecialchars(getPlayableAudioUrl($track['preferred_audio_url'] ?? $track['audio_url'], $track['task_id'] ?? '')) . "', '" . htmlspecialchars($track['title']) . "', '" . htmlspecialchars($track['artist_name']) . "')";
?>
<div class="track-card chart-card" data-track-id="<?= $track['id'] ?>">
<div class="chart-position">#<?= $index + 1 ?></div>
<div class="track-header">
<div class="artist-avatar">
<span class="artist-initial"><?= $artist_initial ?></span>
</div>
<div class="track-info">
<h3 class="track-title">
<a href="/track.php?id=<?= $track['id'] ?>" class="track-title-link"><?= htmlspecialchars($track['title']) ?></a>
</h3>
<p class="track-artist"><?= t('charts.by') ?> <a href="/artist_profile.php?id=<?= $track['user_id'] ?>" class="artist-name-link"><?= htmlspecialchars($track['artist_name']) ?></a></p>
</div>
<div class="track-actions">
<button class="play-btn" onclick="<?= $playFunction ?>" title="<?= $index === 0 ? t('charts.play_chart_playlist') : t('charts.play') ?>">
<i class="fas fa-play"></i>
</button>
</div>
</div>
<div class="track-stats">
<div class="stat-item">
<i class="fas fa-play"></i>
<span><?= number_format($track['play_count']) ?></span>
</div>
<div class="stat-item">
<i class="fas fa-heart"></i>
<span><?= number_format($track['like_count']) ?></span>
</div>
<div class="stat-item">
<i class="fas fa-comment"></i>
<span><?= number_format($track['comment_count']) ?></span>
</div>
</div>
<div class="track-genre">
<span class="genre-tag"><?= htmlspecialchars($metadata['genre']) ?></span>
</div>
<div class="track-duration">
<?= formatDuration($track['preferred_duration'] ?? $track['duration']) ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</section>
<!-- Genres Chart -->
<section id="genres-chart" class="chart-section">
<div class="chart-header">
<h2><i class="fas fa-music"></i> <?= t('charts.trending_genres') ?></h2>
<p><?= t('charts.trending_genres_description') ?></p>
</div>
<div class="genres-grid">
<?php foreach ($trending_genres as $index => $genre): ?>
<div class="genre-card">
<div class="genre-position">#<?= $index + 1 ?></div>
<div class="genre-info">
<h3 class="genre-name"><?= htmlspecialchars($genre['genre']) ?></h3>
<div class="genre-stats">
<div class="stat-item">
<i class="fas fa-heart"></i>
<span><?= number_format($genre['total_likes']) ?></span>
</div>
<div class="stat-item">
<i class="fas fa-play"></i>
<span><?= number_format($genre['total_plays']) ?></span>
</div>
<div class="stat-item">
<i class="fas fa-music"></i>
<span><?= number_format($genre['track_count']) ?> <?= t('charts.tracks') ?></span>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</section>
<!-- Artists Chart -->
<section id="artists-chart" class="chart-section">
<div class="chart-header">
<h2><i class="fas fa-users"></i> <?= t('charts.top_artists') ?></h2>
<p><?= t('charts.top_artists_description') ?></p>
</div>
<div class="artists-grid">
<?php foreach ($top_artists as $index => $artist): ?>
<div class="artist-card">
<div class="artist-position">#<?= $index + 1 ?></div>
<div class="artist-info">
<div class="artist-avatar-large">
<span class="artist-initial"><?= getArtistInitial($artist['artist_name']) ?></span>
</div>
<h3 class="artist-name"><?= htmlspecialchars($artist['artist_name']) ?></h3>
<div class="artist-stats">
<div class="stat-item">
<i class="fas fa-heart"></i>
<span><?= number_format($artist['total_likes']) ?></span>
</div>
<div class="stat-item">
<i class="fas fa-play"></i>
<span><?= number_format($artist['total_plays']) ?></span>
</div>
<div class="stat-item">
<i class="fas fa-music"></i>
<span><?= number_format($artist['track_count']) ?> <?= t('charts.tracks') ?></span>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</section>
</div>
<style>
/* Charts Container */
.charts-container {
min-height: 100vh;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%);
color: white;
}
/* Hero Section */
.charts-hero {
padding: 8rem 0 4rem;
text-align: center;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
position: relative;
overflow: hidden;
}
.charts-hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(102,126,234,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
opacity: 0.3;
}
.hero-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 2rem;
position: relative;
z-index: 2;
}
.hero-title {
font-size: 4rem;
font-weight: 800;
margin-bottom: 1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 4px 8px rgba(102, 126, 234, 0.3);
}
.hero-subtitle {
font-size: 1.4rem;
color: #a0aec0;
margin-bottom: 3rem;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.hero-stats {
display: flex;
justify-content: center;
gap: 4rem;
margin-top: 3rem;
}
.stat-item {
text-align: center;
}
.stat-number {
font-size: 2.5rem;
font-weight: 700;
color: #667eea;
margin-bottom: 0.5rem;
}
.stat-label {
font-size: 1rem;
color: #a0aec0;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Chart Navigation */
.chart-nav {
padding: 2rem 0;
background: rgba(26, 26, 26, 0.8);
backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.nav-tabs {
display: flex;
justify-content: center;
gap: 1rem;
max-width: 1200px;
margin: 0 auto;
padding: 0 2rem;
flex-wrap: wrap;
}
.nav-tab {
padding: 1rem 2rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
color: #a0aec0;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
display: flex;
align-items: center;
gap: 0.5rem;
}
.nav-tab:hover {
background: rgba(102, 126, 234, 0.2);
color: white;
transform: translateY(-2px);
}
.nav-tab.active {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border-color: transparent;
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
}
/* Chart Sections */
.chart-section {
display: none;
padding: 4rem 2rem;
max-width: 1400px;
margin: 0 auto;
}
.chart-section.active {
display: block;
}
.chart-header {
text-align: center;
margin-bottom: 3rem;
}
.chart-header h2 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.chart-header p {
font-size: 1.2rem;
color: #a0aec0;
}
/* Tracks Grid */
.tracks-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
}
/* Chart Cards */
.chart-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 1.5rem;
position: relative;
transition: all 0.3s ease;
backdrop-filter: blur(20px);
}
.chart-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
border-color: rgba(102, 126, 234, 0.3);
}
.chart-position {
position: absolute;
top: -10px;
left: -10px;
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 1.2rem;
color: white;
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
}
.track-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.artist-avatar {
width: 50px;
height: 50px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.5rem;
font-weight: 700;
flex-shrink: 0;
}
.track-info {
flex: 1;
min-width: 0;
}
.track-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 0.25rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.track-title-link {
color: white;
text-decoration: none;
transition: color 0.3s ease;
}
.track-title-link:hover {
color: #667eea;
text-decoration: underline;
}
.track-artist {
font-size: 0.9rem;
color: #a0aec0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.artist-name-link {
color: #a0aec0;
text-decoration: none;
transition: color 0.3s ease;
}
.artist-name-link:hover {
color: #667eea;
text-decoration: underline;
}
.play-btn {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea, #764ba2);
border: none;
border-radius: 10px;
color: white;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.play-btn:hover {
transform: scale(1.1);
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
}
.track-stats {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
.stat-item {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
color: #a0aec0;
}
.stat-item i {
color: #667eea;
}
.track-genre {
margin-bottom: 1rem;
}
.genre-tag {
background: rgba(102, 126, 234, 0.2);
color: #667eea;
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
}
.track-duration {
font-size: 0.9rem;
color: #a0aec0;
text-align: right;
}
/* Genres Grid */
.genres-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 2rem;
}
.genre-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 2rem;
position: relative;
transition: all 0.3s ease;
backdrop-filter: blur(20px);
}
.genre-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
}
.genre-position {
position: absolute;
top: -10px;
left: -10px;
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
color: white;
}
.genre-name {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 1rem;
color: white;
}
.genre-stats {
display: flex;
gap: 1.5rem;
}
/* Artists Grid */
.artists-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 2rem;
}
.artist-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 2rem;
position: relative;
transition: all 0.3s ease;
backdrop-filter: blur(20px);
text-align: center;
}
.artist-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
}
.artist-position {
position: absolute;
top: -10px;
left: -10px;
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
color: white;
}
.artist-avatar-large {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 2rem;
font-weight: 700;
margin: 0 auto 1rem;
}
.artist-name {
font-size: 1.3rem;
font-weight: 700;
margin-bottom: 1rem;
color: white;
}
.artist-stats {
display: flex;
justify-content: center;
gap: 1.5rem;
}
/* Responsive Design */
@media (max-width: 768px) {
.hero-title {
font-size: 2.5rem;
}
.hero-stats {
flex-direction: column;
gap: 2rem;
}
.nav-tabs {
flex-direction: column;
align-items: center;
}
.tracks-grid {
grid-template-columns: 1fr;
}
.genres-grid {
grid-template-columns: 1fr;
}
.artists-grid {
grid-template-columns: 1fr;
}
}
</style>
<script>
// Translation strings for JavaScript
const chartTranslations = {
no_tracks: '<?= addslashes(t('charts.no_tracks_available')) ?>',
player_not_ready: '<?= addslashes(t('charts.player_not_ready')) ?>',
trending: '<?= addslashes(t('charts.trending')) ?>',
most_liked: '<?= addslashes(t('charts.most_liked')) ?>',
most_played: '<?= addslashes(t('charts.most_played')) ?>',
loading_chart: '<?= addslashes(t('charts.loading_chart')) ?>'
};
// Chart tracks data - available for playlist loading
const chartTracksData = {
trending: <?= json_encode(array_map(function($track) {
return [
'id' => $track['id'],
'title' => $track['title'],
'artist_name' => $track['artist_name'],
'audio_url' => getPlayableAudioUrl($track['preferred_audio_url'] ?? $track['audio_url'], $track['task_id'] ?? ''),
'user_id' => $track['user_id'],
'duration' => $track['preferred_duration'] ?? $track['duration']
];
}, $recent_hot_tracks)) ?>,
'most-liked': <?= json_encode(array_map(function($track) {
return [
'id' => $track['id'],
'title' => $track['title'],
'artist_name' => $track['artist_name'],
'audio_url' => getPlayableAudioUrl($track['preferred_audio_url'] ?? $track['audio_url'], $track['task_id'] ?? ''),
'user_id' => $track['user_id'],
'duration' => $track['preferred_duration'] ?? $track['duration']
];
}, $top_liked_tracks)) ?>,
'most-played': <?= json_encode(array_map(function($track) {
return [
'id' => $track['id'],
'title' => $track['title'],
'artist_name' => $track['artist_name'],
'audio_url' => getPlayableAudioUrl($track['preferred_audio_url'] ?? $track['audio_url'], $track['task_id'] ?? ''),
'user_id' => $track['user_id'],
'duration' => $track['preferred_duration'] ?? $track['duration']
];
}, $top_played_tracks)) ?>
};
// Chart Navigation
function showChart(chartType) {
// Hide all chart sections
document.querySelectorAll('.chart-section').forEach(section => {
section.classList.remove('active');
});
// Remove active class from all tabs
document.querySelectorAll('.nav-tab').forEach(tab => {
tab.classList.remove('active');
});
// Show selected chart
document.getElementById(chartType + '-chart').classList.add('active');
// Add active class to clicked tab
event.target.classList.add('active');
}
// Get currently active chart section
function getActiveChartType() {
const activeSection = document.querySelector('.chart-section.active');
if (!activeSection) return 'most-played'; // Default
const sectionId = activeSection.id;
if (sectionId === 'trending-chart') return 'trending';
if (sectionId === 'most-liked-chart') return 'most-liked';
if (sectionId === 'most-played-chart') return 'most-played';
return 'most-played';
}
// Play charts playlist - loads all tracks from current chart section
async function playChartsPlaylist() {
const chartType = getActiveChartType();
const tracks = chartTracksData[chartType] || [];
if (tracks.length === 0) {
if (typeof window.showNotification === 'function') {
window.showNotification(chartTranslations.no_tracks, 'error');
} else {
alert(chartTranslations.no_tracks);
}
return;
}
// Check if global player is available
if (!window.enhancedGlobalPlayer) {
if (typeof window.showNotification === 'function') {
window.showNotification(chartTranslations.player_not_ready, 'error');
} else {
alert(chartTranslations.player_not_ready);
}
return;
}
const chartName = chartType === 'trending' ? chartTranslations.trending :
chartType === 'most-liked' ? chartTranslations.most_liked : chartTranslations.most_played;
if (typeof window.showNotification === 'function') {
window.showNotification('🎵 ' + chartTranslations.loading_chart.replace(':chart', chartName), 'info');
}
try {
// Ensure audio URLs are absolute
const processedTracks = tracks.map(track => {
let audioUrl = track.audio_url;
if (audioUrl && !audioUrl.startsWith('http') && !audioUrl.startsWith('//')) {
if (audioUrl.startsWith('/')) {
audioUrl = window.location.origin + audioUrl;
} else {
audioUrl = window.location.origin + '/' + audioUrl;
}
}
// Debug: Log track info for troubleshooting
if (track.title && track.title.includes('Cracks of Coercion')) {
console.log('🎵 DEBUG - Cracks of Coercion track:', {
title: track.title,
artist: track.artist_name,
audio_url: audioUrl,
duration: track.duration,
id: track.id
});
}
return {
...track,
audio_url: audioUrl
};
});
// Load tracks into global player as a playlist
if (window.enhancedGlobalPlayer.loadArtistPlaylist) {
// Use the artist playlist loader (works for any playlist)
window.enhancedGlobalPlayer.loadArtistPlaylist(processedTracks, chartName + ' Chart', true);
} else {
// Fallback: manually set playlist and play first track
window.enhancedGlobalPlayer.currentPlaylist = processedTracks;
window.enhancedGlobalPlayer.currentPlaylistType = 'charts_' + chartType;
window.enhancedGlobalPlayer.currentTrackIndex = 0;
// Play first track
const firstTrack = processedTracks[0];
if (window.enhancedGlobalPlayer.playTrack) {
window.enhancedGlobalPlayer.playTrack(
firstTrack.audio_url,
firstTrack.title,
firstTrack.artist_name,
firstTrack.id,
firstTrack.user_id
);
}
}
if (typeof window.showNotification === 'function') {
window.showNotification('🎵 Now playing: ' + chartName + ' Chart (' + tracks.length + ' tracks)', 'success');
}
} catch (error) {
console.error('Error loading charts playlist:', error);
if (typeof window.showNotification === 'function') {
window.showNotification('Failed to load charts playlist. Please try again.', 'error');
} else {
alert('Failed to load charts playlist. Please try again.');
}
}
}
// Play track function with URL conversion
// When playing a track from charts, we need to load the charts playlist first
// so that when the track ends, it continues with the charts playlist, not VIP
function playTrack(audioUrl, title, artist) {
console.log('🎵 Charts: playTrack called:', { audioUrl, title, artist });
// Ensure audio URL is absolute if it's relative
let finalAudioUrl = audioUrl;
if (audioUrl && !audioUrl.startsWith('http') && !audioUrl.startsWith('//')) {
if (audioUrl.startsWith('/')) {
finalAudioUrl = window.location.origin + audioUrl;
} else {
finalAudioUrl = window.location.origin + '/' + audioUrl;
}
console.log('🎵 Converted relative URL to absolute:', finalAudioUrl);
}
if (!finalAudioUrl || finalAudioUrl.trim() === '') {
console.error('❌ Audio URL is empty!');
alert('Audio file not available.');
return;
}
// Check if global player is available
if (!window.enhancedGlobalPlayer) {
console.error('❌ Global player not available');
alert('Player not ready. Please try again.');
return;
}
// CRITICAL FIX: Load the charts playlist first, then play the specific track
// This ensures that when the track ends, it continues with the charts playlist, not VIP
const chartType = getActiveChartType();
const tracks = chartTracksData[chartType] || [];
if (tracks.length === 0) {
console.error('❌ No tracks in charts playlist');
alert('No tracks available in chart.');
return;
}
// Process tracks to ensure absolute URLs
const processedTracks = tracks.map(track => {
let trackAudioUrl = track.audio_url;
if (trackAudioUrl && !trackAudioUrl.startsWith('http') && !trackAudioUrl.startsWith('//')) {
if (trackAudioUrl.startsWith('/')) {
trackAudioUrl = window.location.origin + trackAudioUrl;
} else {
trackAudioUrl = window.location.origin + '/' + trackAudioUrl;
}
}
return {
...track,
audio_url: trackAudioUrl
};
});
// Load the charts playlist first (without auto-playing)
const chartName = chartType === 'trending' ? chartTranslations.trending :
chartType === 'most-liked' ? chartTranslations.most_liked : chartTranslations.most_played;
if (window.enhancedGlobalPlayer.loadArtistPlaylist) {
// Load the playlist
window.enhancedGlobalPlayer.loadArtistPlaylist(processedTracks, chartName + ' Chart', false);
// Find the track in the playlist and play it
const trackIndex = processedTracks.findIndex(track =>
track.audio_url === finalAudioUrl ||
(track.title === title && track.artist_name === artist)
);
if (trackIndex !== -1) {
// Set the current track index to the found track
window.enhancedGlobalPlayer.currentTrackIndex = trackIndex;
const track = processedTracks[trackIndex];
// Play the specific track
if (window.enhancedGlobalPlayer.playTrack) {
const success = window.enhancedGlobalPlayer.playTrack(
track.audio_url,
track.title,
track.artist_name,
track.id,
track.user_id
);
if (success) {
console.log('✅ Track playing from charts playlist at index:', trackIndex);
} else {
console.error('❌ Global player failed to play track');
alert('Failed to play track. Please try again.');
}
}
} else {
// Track not found in playlist, play directly (fallback)
console.warn('⚠️ Track not found in charts playlist, playing directly');
if (window.enhancedGlobalPlayer.playTrack) {
window.enhancedGlobalPlayer.playTrack(finalAudioUrl, title, artist);
}
}
} else {
// Fallback: manually set playlist and play track
window.enhancedGlobalPlayer.currentPlaylist = processedTracks;
window.enhancedGlobalPlayer.currentPlaylistType = 'charts_' + chartType;
// Find and play the track
const trackIndex = processedTracks.findIndex(track =>
track.audio_url === finalAudioUrl ||
(track.title === title && track.artist_name === artist)
);
if (trackIndex !== -1) {
window.enhancedGlobalPlayer.currentTrackIndex = trackIndex;
const track = processedTracks[trackIndex];
if (window.enhancedGlobalPlayer.playTrack) {
window.enhancedGlobalPlayer.playTrack(
track.audio_url,
track.title,
track.artist_name,
track.id,
track.user_id
);
}
} else {
// Fallback: play directly
if (window.enhancedGlobalPlayer.playTrack) {
window.enhancedGlobalPlayer.playTrack(finalAudioUrl, title, artist);
}
}
}
}
// Wait for global player function
function waitForGlobalPlayer(callback, maxAttempts = 20) {
if (window.enhancedGlobalPlayer && typeof window.enhancedGlobalPlayer.playTrack === 'function') {
callback();
} else if (maxAttempts > 0) {
setTimeout(() => waitForGlobalPlayer(callback, maxAttempts - 1), 250);
} else {
console.warn('⚠️ Global player not available after maximum attempts');
}
}
// Enable play buttons
function enablePlayButtons() {
console.log('✅ Charts: Play buttons enabled');
// Play buttons are already functional via onclick handlers
}
// Initialize global player integration
document.addEventListener('DOMContentLoaded', function() {
console.log('🎵 Charts page loaded, waiting for global player...');
// Wait for global player to be ready, then enable play buttons
waitForGlobalPlayer(() => {
enablePlayButtons();
});
});
</script>
<?php include 'includes/footer.php'; ?>