![]() 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/-1ab10e94/ |
<?php
// New Enhanced Global Music Player - Based on test_enhanced_player.php
// Clean, modern SoundCloud-style bottom player
// Prevent multiple includes
if (defined('GLOBAL_PLAYER_INCLUDED')) {
return;
}
define('GLOBAL_PLAYER_INCLUDED', true);
?>
<!-- Enhanced Global Music Player -->
<div id="enhancedGlobalPlayer" class="global-bottom-player">
<!-- Track Info Section -->
<div class="player-track-info">
<div class="track-details">
<div class="track-title clickable" id="playerTrackTitle" title="Click to view track details">Loading playlists...</div>
<div class="track-artist clickable" id="playerTrackArtist" title="Click to view artist profile">Discovering great music for you</div>
</div>
</div>
<!-- Playlist Selector -->
<div class="playlist-selector" id="playlistSelector">
<select id="playlistDropdown" class="playlist-dropdown" onchange="switchPlaylist(this.value)">
<option value="vip">π΅ VIP Samples</option>
<option value="featured">β Featured</option>
<option value="community">π Community</option>
</select>
<button class="player-btn shuffle-btn" id="playerShuffleBtn" title="Shuffle Playlist" onclick="window.enhancedGlobalPlayer.playRandomTrack()">
<i class="fas fa-random"></i>
</button>
</div>
<!-- Main Player Controls -->
<div class="player-main-controls">
<button class="player-btn previous-btn" id="playerPrevBtn" title="Previous">
<i class="fas fa-step-backward"></i>
</button>
<button class="player-btn play-pause-btn" id="playerPlayBtn" title="Play">
<i class="fas fa-play" id="playerPlayIcon"></i>
</button>
<button class="player-btn next-btn" id="playerNextBtn" title="Next">
<i class="fas fa-step-forward"></i>
</button>
</div>
<!-- Progress Section with Interactive Waveform -->
<div class="player-progress-section">
<span class="time-current" id="playerTimeDisplay">0:00</span>
<div class="progress-container" id="playerProgressContainer">
<!-- Traditional Progress Bar (fallback) -->
<div class="progress-bar-background"></div>
<div class="progress-bar" id="playerProgressBar"></div>
<!-- Interactive Waveform representing the actual song -->
<div class="waveform-progress-container" id="waveformProgressContainer">
<div class="waveform-progress-bars" id="waveformProgressBars">
<!-- Waveform bars representing actual song amplitude -->
</div>
<div class="waveform-progress-line" id="waveformProgressLine"></div>
</div>
<div class="progress-handle" id="playerProgressHandle"></div>
</div>
<span class="time-duration" id="playerDurationDisplay">0:00</span>
</div>
<!-- Advanced Controls -->
<div class="player-advanced-controls">
<div class="volume-control-wrapper">
<button class="player-btn volume-btn" id="playerVolumeBtn" title="Click to mute/unmute">
<i class="fas fa-volume-up" id="playerVolumeIcon"></i>
</button>
<div class="volume-slider-container" id="volumeSliderContainer">
<input type="range" class="volume-slider" id="playerVolumeSlider" min="0" max="100" value="50" orient="horizontal">
<div class="volume-display" id="volumeDisplay">50%</div>
</div>
</div>
<button class="player-btn speed-btn" id="playerSpeedBtn" title="Speed: 1x">
<span class="speed-text">1x</span>
</button>
<button class="player-btn auto-play-btn" id="playerAutoPlayBtn" title="Auto-play: On">
<i class="fas fa-infinity" id="playerAutoPlayIcon"></i>
</button>
<button class="player-btn minimize-btn" id="playerMinimizeBtn" title="Minimize">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<!-- Waveform Visualizer -->
<div class="waveform-container" id="playerWaveformContainer">
<div class="waveform-bars" id="playerWaveformBars">
<!-- Animated waveform bars will be generated dynamically -->
</div>
</div>
<!-- Hidden Audio Element -->
<audio id="globalAudioElement" preload="none"></audio>
</div>
<style>
/* Enhanced CSS Variables from test_enhanced_player.php */
:root {
--primary: #667eea;
--primary-dark: #5a67d8;
--primary-light: #7c3aed;
--secondary: #764ba2;
--secondary-dark: #6b46c1;
--secondary-light: #8b5cf6;
--accent: #4facfe;
--accent-dark: #3b82f6;
--accent-light: #60a5fa;
--gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--gradient-secondary: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
--gradient-accent: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
--gradient-divine: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--bg-primary: #0a0a0a;
--bg-secondary: #1a1a1a;
--bg-tertiary: #2d2d2d;
--bg-quaternary: #3a3a3a;
--bg-card: rgba(26, 26, 26, 0.9);
--bg-overlay: rgba(0, 0, 0, 0.8);
--bg-glass: rgba(255, 255, 255, 0.05);
--text-primary: #ffffff;
--text-secondary: #a0aec0;
--text-muted: #718096;
--text-light: #4a5568;
--text-accent: #4facfe;
--border-light: rgba(255, 255, 255, 0.1);
--border-medium: rgba(255, 255, 255, 0.2);
--border-accent: rgba(102, 126, 234, 0.3);
--shadow-light: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
--shadow-medium: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
--shadow-heavy: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
--shadow-glow: 0 0 20px rgba(102, 126, 234, 0.3);
}
/* Enhanced SoundCloud-Style Global Player */
.global-bottom-player {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(135deg, rgba(26, 26, 26, 0.98) 0%, rgba(45, 45, 45, 0.98) 100%);
backdrop-filter: blur(20px);
border-top: 2px solid var(--border-accent);
padding: 8px 16px;
display: flex;
align-items: center;
gap: 16px;
z-index: 10000;
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.6), 0 -2px 8px rgba(102, 126, 234, 0.2);
height: 60px;
min-height: 60px;
max-height: 60px;
transition: all 0.4s ease;
overflow: visible;
}
/* Mobile Responsive Design - Clean & Organized */
@media (max-width: 768px) {
.global-bottom-player {
height: auto;
min-height: 80px;
max-height: 120px;
padding: 12px 16px;
flex-wrap: wrap;
gap: 12px;
overflow: visible;
}
.global-bottom-player.playing {
height: auto;
min-height: 80px;
max-height: 120px;
}
.global-bottom-player.minimized {
height: 50px;
min-height: 50px;
max-height: 50px;
padding: 8px 12px;
flex-wrap: nowrap;
}
/* Mobile Track Info */
.player-track-info {
min-width: 0;
flex: 1;
order: 1;
width: 100%;
margin-bottom: 8px;
}
.track-details {
min-width: 0;
width: 100%;
}
.track-title {
font-size: 13px;
line-height: 1.1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.track-artist {
font-size: 11px;
line-height: 1.1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Mobile Controls Layout */
.player-main-controls {
order: 2;
gap: 12px;
justify-content: center;
width: 100%;
margin-bottom: 8px;
}
.player-btn {
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
touch-action: manipulation;
}
.play-pause-btn {
width: 40px;
height: 40px;
min-width: 40px;
min-height: 40px;
}
/* Mobile Progress Section */
.player-progress-section {
order: 3;
max-width: 100%;
margin: 0;
gap: 10px;
width: 100%;
}
.time-current,
.time-duration {
font-size: 11px;
min-width: 25px;
}
.progress-container {
height: 16px;
min-height: 16px;
}
.progress-bar {
height: 6px;
min-height: 6px;
}
.progress-handle {
width: 16px;
height: 16px;
min-width: 16px;
min-height: 16px;
}
/* Mobile Advanced Controls */
.player-advanced-controls {
order: 4;
display: flex;
align-items: center;
gap: 8px;
justify-content: center;
width: 100%;
flex-wrap: wrap;
}
.volume-control-wrapper {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.volume-btn {
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
}
.volume-slider-container {
width: 60px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.volume-slider {
width: 100%;
height: 6px;
min-height: 6px;
-webkit-appearance: none;
appearance: none;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
outline: none;
cursor: pointer;
}
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
min-width: 16px;
min-height: 16px;
background: var(--gradient-primary);
border-radius: 50%;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
border: 2px solid white;
}
.volume-slider::-moz-range-thumb {
width: 16px;
height: 16px;
min-width: 16px;
min-height: 16px;
background: var(--gradient-primary);
border-radius: 50%;
cursor: pointer;
border: 2px solid white;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
.volume-display {
font-size: 10px;
color: var(--text-secondary);
text-align: center;
min-width: 40px;
padding: 2px 6px;
background: rgba(0, 0, 0, 0.4);
border-radius: 4px;
font-weight: 500;
border: 1px solid var(--border-light);
}
/* Other control buttons */
.speed-btn,
.auto-play-btn,
.minimize-btn {
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
font-size: 12px;
}
.speed-btn .speed-text {
font-size: 10px;
font-weight: 600;
}
/* Mobile Playlist Selector */
.playlist-selector {
order: 5;
margin: 0;
width: 100%;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.playlist-dropdown {
font-size: 11px;
padding: 6px 10px;
background: var(--bg-glass);
border: 1px solid var(--border-light);
border-radius: 6px;
color: var(--text-primary);
cursor: pointer;
min-height: 36px;
min-width: 100px;
font-weight: 500;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6,9 12,15 18,9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 12px;
padding-right: 28px;
}
.shuffle-btn {
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
background: var(--bg-glass);
border: 1px solid var(--border-light);
color: var(--text-secondary);
transition: all 0.3s ease;
font-size: 12px;
}
.shuffle-btn:hover {
background: var(--primary);
color: white;
border-color: var(--primary);
transform: scale(1.05);
}
/* Mobile Waveform */
.waveform-container {
order: 6;
width: 100%;
height: 32px;
margin-top: 8px;
padding: 0 8px;
}
.waveform-bars {
height: 100%;
gap: 3px;
display: flex;
align-items: flex-end;
justify-content: space-between;
}
.waveform-bar {
min-width: 4px;
max-width: 8px;
border-radius: 2px;
background: var(--text-secondary);
flex: 1;
transition: all 0.2s ease;
animation: waveAnimation 1.5s ease-in-out infinite alternate;
}
.waveform-bar.playing {
background: var(--primary);
box-shadow: 0 0 8px rgba(102, 126, 234, 0.6);
transform: scaleY(1.2);
}
.waveform-bar.played {
background: var(--accent);
box-shadow: 0 0 4px rgba(79, 172, 254, 0.4);
}
/* Waveform animation keyframes */
@keyframes waveAnimation {
0% {
height: 25%;
opacity: 0.6;
}
100% {
height: 120%;
opacity: 1;
}
}
/* Even bars animation delay */
.waveform-bar:nth-child(even) {
animation-delay: 0.1s;
}
.waveform-bar:nth-child(3n) {
animation-delay: 0.2s;
}
.waveform-bar:nth-child(4n) {
animation-delay: 0.3s;
}
.waveform-bar:nth-child(5n) {
animation-delay: 0.4s;
}
/* Enhanced touch feedback for mobile */
.player-btn:active {
transform: scale(0.95);
transition: transform 0.1s ease;
}
/* Hide some elements on very small screens */
@media (max-width: 480px) {
.global-bottom-player {
padding: 10px 12px;
gap: 8px;
}
.player-advanced-controls {
gap: 6px;
}
.player-btn {
width: 28px;
height: 28px;
min-width: 28px;
min-height: 28px;
}
.play-pause-btn {
width: 36px;
height: 36px;
min-width: 36px;
min-height: 36px;
}
.track-title {
font-size: 12px;
}
.track-artist {
font-size: 10px;
}
.playlist-dropdown {
min-width: 80px;
font-size: 10px;
padding: 4px 8px;
}
.volume-slider-container {
width: 50px;
}
}
/* Extra small screens */
@media (max-width: 360px) {
.global-bottom-player {
padding: 8px 10px;
gap: 6px;
}
.player-btn {
width: 24px;
height: 24px;
min-width: 24px;
min-height: 24px;
}
.play-pause-btn {
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
}
.track-title {
font-size: 11px;
}
.track-artist {
font-size: 9px;
}
.playlist-dropdown {
min-width: 70px;
font-size: 9px;
padding: 3px 6px;
}
}
/* Landscape orientation adjustments */
@media (max-width: 768px) and (orientation: landscape) {
.global-bottom-player {
height: auto;
min-height: 60px;
max-height: 80px;
flex-wrap: nowrap;
padding: 8px 16px;
gap: 12px;
}
.player-track-info {
order: 1;
width: auto;
margin-bottom: 0;
min-width: 180px;
max-width: 220px;
}
.player-main-controls {
order: 2;
width: auto;
margin-bottom: 0;
gap: 8px;
}
.player-progress-section {
order: 3;
width: auto;
flex: 1;
min-width: 120px;
gap: 8px;
}
.player-advanced-controls {
order: 4;
width: auto;
gap: 6px;
}
.playlist-selector {
order: 5;
width: auto;
margin: 0;
}
.waveform-container {
display: none; /* Hide waveform in landscape to save space */
}
/* Adjust button sizes for landscape */
.player-btn {
width: 28px;
height: 28px;
min-width: 28px;
min-height: 28px;
}
.play-pause-btn {
width: 36px;
height: 36px;
min-width: 36px;
min-height: 36px;
}
.time-current,
.time-duration {
font-size: 10px;
min-width: 24px;
}
.playlist-dropdown {
min-width: 100px;
font-size: 10px;
padding: 4px 8px;
}
.volume-slider-container {
width: 50px;
}
}
}
.global-bottom-player::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
background: linear-gradient(90deg,
transparent 0%,
rgba(102, 126, 234, 0.05) 25%,
rgba(118, 75, 162, 0.05) 75%,
transparent 100%);
pointer-events: none;
animation: ambientGlow 8s ease-in-out infinite;
}
@keyframes ambientGlow {
0%, 100% { opacity: 0.3; transform: translateX(-100%); }
50% { opacity: 0.7; transform: translateX(100%); }
}
.global-bottom-player.playing {
display: flex !important;
animation: slideUp 0.5s cubic-bezier(0.4, 0, 0.2, 1);
background: linear-gradient(135deg, rgba(26, 26, 26, 0.98) 0%, rgba(45, 45, 45, 0.98) 50%, rgba(102, 126, 234, 0.15) 100%);
box-shadow: 0 -8px 40px rgba(102, 126, 234, 0.4), 0 -4px 20px rgba(118, 75, 162, 0.3);
border-top-color: rgba(102, 126, 234, 0.6);
}
/* Ensure player is always visible */
.global-bottom-player {
display: flex !important;
}
.global-bottom-player.minimized {
height: 40px;
min-height: 40px;
max-height: 40px;
padding: 6px 12px;
}
@keyframes slideUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
/* Enhanced Track Info */
.player-track-info {
display: flex !important;
align-items: center;
gap: 8px;
flex-shrink: 0;
position: relative;
min-width: 250px;
visibility: visible !important;
opacity: 1 !important;
}
.track-details {
min-width: 200px;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.track-title {
color: var(--text-primary);
font-weight: 700;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 2px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
line-height: 1.2;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.track-artist {
color: var(--text-secondary);
font-size: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
line-height: 1.2;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
/* Clickable track title and artist styles */
.track-title.clickable,
.track-artist.clickable {
cursor: pointer;
transition: all 0.3s ease;
position: relative;
z-index: 10;
pointer-events: auto;
user-select: none;
}
.track-title.clickable:hover,
.track-artist.clickable:hover {
color: var(--primary);
text-shadow: 0 0 8px rgba(102, 126, 234, 0.5);
transform: translateY(-1px);
}
.track-title.clickable::after {
content: "π";
position: absolute;
right: -20px;
top: 50%;
transform: translateY(-50%);
font-size: 12px;
opacity: 0;
transition: opacity 0.3s ease;
}
.track-title.clickable:hover::after {
opacity: 1;
}
.track-artist.clickable::after {
content: "π€";
position: absolute;
right: -20px;
top: 50%;
transform: translateY(-50%);
font-size: 12px;
opacity: 0;
transition: opacity 0.3s ease;
}
.track-artist.clickable:hover::after {
opacity: 1;
}
.track-title-link {
color: inherit;
text-decoration: none;
transition: all 0.3s ease;
cursor: pointer;
display: block;
width: 100%;
}
.track-title-link:hover {
color: var(--primary);
text-decoration: underline;
text-shadow: 0 0 8px rgba(102, 126, 234, 0.5);
}
.track-artist-link {
color: inherit;
text-decoration: none;
transition: all 0.3s ease;
cursor: pointer;
display: block;
width: 100%;
}
.track-artist-link:hover {
color: var(--primary);
text-decoration: underline;
text-shadow: 0 0 8px rgba(102, 126, 234, 0.5);
}
/* Playlist Selector */
.playlist-selector {
margin-left: 8px;
}
.playlist-dropdown {
background: transparent;
border: none;
color: var(--text-secondary);
padding: 0;
font-size: 10px;
cursor: pointer;
outline: none;
transition: all 0.2s ease;
}
.playlist-dropdown:hover {
color: var(--text-primary);
}
.playlist-dropdown:focus {
color: var(--text-primary);
}
.playlist-dropdown option {
background: var(--bg-secondary);
color: var(--text-primary);
}
/* Main Controls */
.player-main-controls {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.player-btn {
background: var(--bg-glass);
border: 1px solid var(--border-light);
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-primary);
cursor: pointer;
transition: all 0.2s ease;
backdrop-filter: blur(10px);
}
.player-btn:hover {
background: var(--border-medium);
border-color: var(--border-medium);
transform: scale(1.05);
box-shadow: var(--shadow-glow);
}
.play-pause-btn {
width: 30px;
height: 30px;
background: linear-gradient(135deg, var(--primary), var(--secondary));
border: none;
box-shadow: var(--shadow-light);
}
.play-pause-btn:hover {
transform: scale(1.1);
box-shadow: var(--shadow-glow);
}
.auto-play-btn {
position: relative;
}
.auto-play-btn.active {
background: var(--gradient-divine);
border-color: var(--primary);
box-shadow: var(--shadow-glow);
}
.auto-play-btn.active:hover {
transform: scale(1.05);
box-shadow: 0 0 15px rgba(102, 126, 234, 0.6);
}
/* Progress Section */
.player-progress-section {
flex: 1;
display: flex;
align-items: center;
gap: 15px;
max-width: 600px;
margin: 0 20px;
}
.time-current,
.time-duration {
font-size: 12px;
color: var(--text-secondary);
font-weight: 500;
min-width: 30px;
text-align: center;
}
.progress-container {
flex: 1;
height: 20px;
background: transparent;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.progress-container:hover {
transform: scaleY(1.05);
}
.progress-bar-background {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
}
.progress-bar {
position: absolute;
bottom: 0;
left: 0;
height: 4px;
background: var(--gradient-divine);
border-radius: 2px;
width: 0%;
transition: width 0.1s ease;
box-shadow: 0 0 10px rgba(102, 126, 234, 0.5);
z-index: 3;
}
/* Interactive Waveform Progress */
.waveform-progress-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 6px;
display: flex;
align-items: end;
overflow: hidden;
}
.waveform-progress-bars {
display: flex;
align-items: end;
height: 120px;
width: 100%;
gap: 1px;
}
.waveform-progress-bar {
background: rgba(255, 255, 255, 0.3);
flex: 1;
min-height: 8px;
max-height: 120px;
border-radius: 2px;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
}
.waveform-progress-bar::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transform: translateX(-100%);
transition: transform 0.5s ease;
}
.waveform-progress-bar:hover {
background: rgba(255, 255, 255, 0.6);
transform: scaleY(1.2);
box-shadow: 0 0 8px rgba(255, 255, 255, 0.4);
}
.waveform-progress-bar:hover::before {
transform: translateX(100%);
}
.waveform-progress-bar.played {
background: var(--gradient-primary);
box-shadow: 0 0 6px rgba(102, 126, 234, 0.5);
transform: scaleY(1.1);
}
.waveform-progress-bar.playing {
background: var(--accent);
box-shadow: 0 0 12px rgba(79, 172, 254, 0.8);
transform: scaleY(1.4);
animation: playingPulse 0.8s ease-in-out infinite alternate;
}
@keyframes playingPulse {
0% {
transform: scaleY(1.2);
box-shadow: 0 0 8px rgba(79, 172, 254, 0.6);
}
100% {
transform: scaleY(1.6);
box-shadow: 0 0 16px rgba(79, 172, 254, 1);
}
}
.waveform-progress-line {
position: absolute;
top: 0;
bottom: 6px;
width: 2px;
background: var(--text-primary);
box-shadow: 0 0 8px rgba(255, 255, 255, 0.8);
border-radius: 1px;
left: 0%;
transition: left 0.1s ease;
z-index: 4;
}
.progress-handle {
position: absolute;
bottom: -4px;
width: 14px;
height: 14px;
background: var(--text-primary);
border-radius: 50%;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4), 0 0 0 2px var(--primary);
left: 0%;
transform: translateX(-50%);
opacity: 0;
transition: all 0.3s ease;
z-index: 5;
}
.progress-container:hover .progress-handle {
opacity: 1;
transform: translateX(-50%) scale(1.2);
}
/* Advanced Controls */
.player-advanced-controls {
display: flex;
align-items: center;
gap: 10px;
position: relative;
z-index: 10000;
flex-shrink: 0;
}
.volume-btn,
.speed-btn,
.minimize-btn {
width: 24px;
height: 24px;
position: relative;
}
.volume-control-wrapper {
position: relative;
display: inline-block;
}
.volume-btn {
background: var(--bg-glass);
border: 1px solid var(--border-light);
border-radius: 50%;
transition: all 0.2s ease;
}
.volume-btn:hover {
background: var(--border-medium);
border-color: var(--border-medium);
transform: scale(1.05);
box-shadow: var(--shadow-glow);
}
.speed-btn {
border-radius: 6px;
width: 30px;
}
.speed-text {
font-size: 10px;
font-weight: 600;
}
.volume-slider-container {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%) translateY(10px);
background: var(--bg-tertiary);
border: 1px solid var(--border-light);
border-radius: 8px;
padding: 10px 8px;
backdrop-filter: blur(20px);
box-shadow: var(--shadow-heavy);
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
z-index: 10001;
min-width: 40px;
min-height: 100px;
}
.volume-slider-container.active {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(-10px);
z-index: 10001;
}
.volume-slider {
writing-mode: bt-lr; /* IE */
-webkit-appearance: slider-vertical; /* WebKit */
width: 16px;
height: 60px;
background: var(--bg-glass);
outline: none;
cursor: pointer;
border-radius: 8px;
-webkit-appearance: none;
appearance: none;
}
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
background: var(--primary);
border-radius: 50%;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.volume-slider::-moz-range-thumb {
width: 16px;
height: 16px;
background: var(--primary);
border-radius: 50%;
cursor: pointer;
border: none;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
/* Waveform Visualizer */
.waveform-container {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 8px;
overflow: hidden;
opacity: 0.8;
pointer-events: none;
}
.waveform-bars {
display: flex;
align-items: end;
height: 100%;
gap: 2px;
justify-content: space-between;
}
.waveform-bar {
background: var(--gradient-divine);
flex: 1;
min-height: 2px;
max-width: 4px;
border-radius: 1px;
transition: all 0.3s ease;
animation: waveAnimation 1.5s ease-in-out infinite alternate;
}
@keyframes waveAnimation {
0% {
height: 20%;
opacity: 0.6;
filter: hue-rotate(0deg);
}
100% {
height: 100%;
opacity: 1;
filter: hue-rotate(10deg);
}
}
.waveform-bar:nth-child(even) {
animation-delay: 0.1s;
}
.waveform-bar:nth-child(3n) {
animation-delay: 0.2s;
}
.waveform-bar:nth-child(4n) {
animation-delay: 0.3s;
}
.waveform-bar:nth-child(5n) {
animation-delay: 0.4s;
}
/* Enhanced Playing State */
.global-bottom-player.playing .waveform-bar {
animation: wavePlayback 0.8s ease-in-out infinite alternate;
background: linear-gradient(45deg, var(--gradient-divine), var(--accent));
box-shadow: 0 0 5px rgba(102, 126, 234, 0.5);
}
@keyframes wavePlayback {
0% {
transform: scaleY(0.4);
opacity: 0.7;
filter: hue-rotate(0deg) brightness(1);
}
100% {
transform: scaleY(1.3);
opacity: 1;
filter: hue-rotate(20deg) brightness(1.2);
}
}
.global-bottom-player.playing .play-pause-btn {
animation: playButtonGlow 3s ease-in-out infinite alternate;
}
@keyframes playButtonGlow {
0% {
box-shadow: var(--shadow-glow), var(--shadow-medium);
filter: brightness(1);
}
100% {
box-shadow: 0 0 30px rgba(102, 126, 234, 0.8), 0 8px 30px rgba(118, 75, 162, 0.6);
filter: brightness(1.2);
}
}
.volume-slider-container {
</style>
<script>
// Enhanced Global Player with AJAX Navigation Support
(function() {
'use strict';
// Global player state
let currentTrack = null;
let currentPlaylist = 'vip';
let isPlaying = false;
let currentTime = 0;
let duration = 0;
let volume = 0.5;
let previousVolume = 0.5; // Store volume before muting
let isMuted = false;
let playbackRate = 1.0;
let autoPlay = true;
let isMinimized = false;
// Initialize global player
function initializeGlobalPlayer() {
console.log('π΅ Initializing Enhanced Global Player');
try {
// Set up audio element
const audio = document.getElementById('globalAudioElement');
if (!audio) {
console.error('β Global audio element not found');
return false;
}
// Mobile-specific audio setup
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if (isMobile) {
console.log('π± Mobile device detected, setting up mobile audio');
// Set mobile-specific audio attributes
audio.preload = 'metadata';
audio.playsInline = true;
audio.webkitPlaysinline = true;
audio.mozPlaysinline = true;
// Mobile audio context handling
if (typeof window.AudioContext !== 'undefined' || typeof window.webkitAudioContext !== 'undefined') {
console.log('π± Web Audio API available');
}
// Set initial volume for mobile
volume = 0.7; // Slightly higher default for mobile
audio.volume = volume;
}
// Set up event listeners
setupAudioEventListeners(audio);
// Set up player controls
setupPlayerControls();
// Set up playlist functionality
setupPlaylistFunctionality();
// Load initial playlist
loadPlaylist(currentPlaylist);
// Generate initial waveform visualizer
generateWaveform();
generateInteractiveWaveform(180);
// Mark player as ready
window.globalPlayerReady = true;
// Mobile-specific ready notification
if (isMobile) {
console.log('π± Mobile player ready');
showNotification('π΅ Mobile player ready! Tap play to start', 'success');
}
console.log('β
Enhanced Global Player initialized successfully');
return true;
} catch (error) {
console.error('β Global player initialization failed:', error);
return false;
}
}
// Set up audio event listeners
function setupAudioEventListeners(audio) {
audio.addEventListener('loadedmetadata', () => {
duration = audio.duration;
updateDurationDisplay();
updateProgressBar();
});
audio.addEventListener('timeupdate', () => {
currentTime = audio.currentTime;
updateTimeDisplay();
updateProgressBar();
updateWaveformProgress(currentTime, duration);
});
audio.addEventListener('ended', () => {
if (autoPlay) {
playNextTrack();
} else {
stopPlayback();
}
});
audio.addEventListener('error', (e) => {
console.error('β Audio error:', e);
showNotification('Audio playback error', 'error');
});
}
// Set up player controls
function setupPlayerControls() {
// Play/Pause button
const playBtn = document.getElementById('playerPlayBtn');
if (playBtn) {
playBtn.addEventListener('click', togglePlayPause);
// Add touch feedback for mobile
addTouchFeedback(playBtn);
}
// Previous/Next buttons
const prevBtn = document.getElementById('playerPrevBtn');
const nextBtn = document.getElementById('playerNextBtn');
if (prevBtn) {
prevBtn.addEventListener('click', playPreviousTrack);
addTouchFeedback(prevBtn);
}
if (nextBtn) {
nextBtn.addEventListener('click', playNextTrack);
addTouchFeedback(nextBtn);
}
// Volume control
const volumeSlider = document.getElementById('playerVolumeSlider');
if (volumeSlider) {
volumeSlider.addEventListener('input', (e) => {
volume = e.target.value / 100;
updateVolume();
});
// Mouse wheel support for volume
volumeSlider.addEventListener('wheel', (e) => {
e.preventDefault();
const delta = e.deltaY > 0 ? -0.05 : 0.05;
volume = Math.max(0, Math.min(1, volume + delta));
volumeSlider.value = volume * 100;
updateVolume();
});
// Click to seek in volume slider
volumeSlider.addEventListener('click', (e) => {
const rect = volumeSlider.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const width = rect.width;
volume = clickX / width;
volumeSlider.value = volume * 100;
updateVolume();
});
// Touch-friendly volume control for mobile
addTouchVolumeControl(volumeSlider);
}
// Volume button (mute/unmute)
const volumeBtn = document.getElementById('playerVolumeBtn');
if (volumeBtn) {
volumeBtn.addEventListener('click', toggleVolumeSlider);
addTouchFeedback(volumeBtn);
}
// Progress bar with enhanced mobile support
const progressContainer = document.getElementById('playerProgressContainer');
if (progressContainer) {
progressContainer.addEventListener('click', seekToPosition);
// Add touch support for mobile
addTouchProgressControl(progressContainer);
}
// Minimize button
const minimizeBtn = document.getElementById('playerMinimizeBtn');
if (minimizeBtn) {
minimizeBtn.addEventListener('click', toggleMinimize);
addTouchFeedback(minimizeBtn);
}
// Speed button
const speedBtn = document.getElementById('playerSpeedBtn');
if (speedBtn) {
speedBtn.addEventListener('click', cyclePlaybackSpeed);
addTouchFeedback(speedBtn);
}
// Auto-play button
const autoPlayBtn = document.getElementById('playerAutoPlayBtn');
if (autoPlayBtn) {
autoPlayBtn.addEventListener('click', toggleAutoPlay);
addTouchFeedback(autoPlayBtn);
}
// Shuffle button
const shuffleBtn = document.getElementById('playerShuffleBtn');
if (shuffleBtn) {
addTouchFeedback(shuffleBtn);
}
// Track title click - navigate to track page
const trackTitle = document.getElementById('playerTrackTitle');
if (trackTitle) {
console.log('π΅ Setting up track title click handler');
trackTitle.addEventListener('click', function(e) {
console.log('π΅ Track title clicked!');
e.preventDefault();
e.stopPropagation();
const title = trackTitle.textContent;
console.log('π΅ Title text:', title);
if (title && title !== 'Loading playlists...') {
console.log('π΅ Looking for track:', title);
console.log('π΅ Debugging track ID lookup...');
console.log('π΅ currentTrack object:', currentTrack);
console.log('π΅ window.currentPlaylist:', window.currentPlaylist);
console.log('π΅ window.currentPlaylistData:', window.currentPlaylistData);
// Try multiple ways to find the track ID
let trackId = null;
// Method 1: Check if we have a current track with ID (most reliable)
if (currentTrack && currentTrack.id) {
trackId = currentTrack.id;
console.log('π΅ Found track ID from currentTrack:', trackId);
}
// Method 2: Check currentPlaylistData (the actual track array)
else if (window.currentPlaylistData && window.currentPlaylistData.length > 0) {
console.log('π΅ Searching in currentPlaylistData:', window.currentPlaylistData);
const foundTrack = window.currentPlaylistData.find(track =>
track.title === title || track.audio_url === currentTrack?.audioUrl
);
if (foundTrack && foundTrack.id) {
trackId = foundTrack.id;
console.log('π΅ Found track ID from currentPlaylistData:', trackId);
} else {
console.log('π΅ Track not found in currentPlaylistData');
}
}
// Method 3: Check if we can extract from the audio URL
else if (currentTrack && currentTrack.audioUrl) {
console.log('π΅ Trying to extract from audio URL:', currentTrack.audioUrl);
// Try to extract track ID from URL patterns
const urlMatch = currentTrack.audioUrl.match(/(\d+)\.mp3$/);
if (urlMatch) {
trackId = urlMatch[1];
console.log('π΅ Extracted track ID from URL:', trackId);
}
}
console.log('π΅ Final trackId found:', trackId);
if (trackId) {
console.log('π΅ Navigating to track page with ID:', trackId);
window.location.href = `/track.php?id=${trackId}`;
} else {
console.log('π΅ No track ID found, navigating to library');
window.location.href = `/library.php`;
}
} else {
console.log('π΅ Title is loading or empty');
}
});
// Add touch feedback for mobile
addTouchFeedback(trackTitle);
} else {
console.log('π΅ Track title element not found');
}
// Track artist click - navigate to artist profile
const trackArtist = document.getElementById('playerTrackArtist');
if (trackArtist) {
console.log('π΅ Setting up track artist click handler');
trackArtist.addEventListener('click', function(e) {
console.log('π΅ Track artist clicked!');
e.preventDefault();
e.stopPropagation();
const artist = trackArtist.textContent;
console.log('π΅ Artist text:', artist);
if (artist && artist !== 'Discovering great music for you') {
console.log('π΅ Navigating to artists page for:', artist);
// Navigate to artists page
window.location.href = `/artists.php`;
} else {
console.log('π΅ Artist is loading or empty');
}
});
// Add touch feedback for mobile
addTouchFeedback(trackArtist);
} else {
console.log('π΅ Track artist element not found');
}
// Add mobile-specific event listeners
setupMobileControls();
}
// Mobile-specific touch feedback and controls
function addTouchFeedback(element) {
if (!element) return;
// Add touch feedback for mobile devices
element.addEventListener('touchstart', function() {
this.style.transform = 'scale(0.95)';
this.style.transition = 'transform 0.1s ease';
});
element.addEventListener('touchend', function() {
this.style.transform = 'scale(1)';
});
// Don't prevent default for interactive elements to ensure proper functionality
if (!element.classList.contains('player-btn') && !element.classList.contains('clickable')) {
element.addEventListener('touchend', function(e) {
e.preventDefault();
});
}
}
// Enhanced volume control for mobile
function addTouchVolumeControl(volumeSlider) {
if (!volumeSlider) return;
let isDragging = false;
let startY = 0;
let startVolume = 0;
volumeSlider.addEventListener('touchstart', function(e) {
isDragging = true;
startY = e.touches[0].clientY;
startVolume = volume;
// Don't prevent default for volume slider
});
volumeSlider.addEventListener('touchmove', function(e) {
if (!isDragging) return;
const currentY = e.touches[0].clientY;
const deltaY = startY - currentY;
const volumeChange = deltaY / 100; // Adjust sensitivity
volume = Math.max(0, Math.min(1, startVolume + volumeChange));
volumeSlider.value = volume * 100;
updateVolume();
e.preventDefault(); // Only prevent default for touchmove
});
volumeSlider.addEventListener('touchend', function() {
isDragging = false;
});
}
// Enhanced progress control for mobile
function addTouchProgressControl(progressContainer) {
if (!progressContainer) return;
let isDragging = false;
let startX = 0;
let startTime = 0;
progressContainer.addEventListener('touchstart', function(e) {
isDragging = true;
const rect = this.getBoundingClientRect();
startX = e.touches[0].clientX - rect.left;
startTime = currentTime;
// Don't prevent default for progress bar
});
progressContainer.addEventListener('touchmove', function(e) {
if (!isDragging) return;
const rect = this.getBoundingClientRect();
const currentX = e.touches[0].clientX - rect.left;
const width = rect.width;
const percentage = Math.max(0, Math.min(1, currentX / width));
const seekTime = percentage * duration;
if (seekTime >= 0 && seekTime <= duration) {
const audio = document.getElementById('globalAudioElement');
if (audio) {
audio.currentTime = seekTime;
currentTime = seekTime;
updateTimeDisplay();
updateProgressBar();
}
}
e.preventDefault(); // Only prevent default for touchmove
});
progressContainer.addEventListener('touchend', function() {
isDragging = false;
});
}
// Setup mobile-specific controls and behaviors
function setupMobileControls() {
// Detect mobile device
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if (isMobile) {
console.log('π± Mobile device detected, setting up mobile controls');
// Add mobile-specific event listeners with proper passive handling
document.addEventListener('touchstart', handleMobileTouchStart, { passive: true });
document.addEventListener('touchend', handleMobileTouchEnd, { passive: true });
// Add swipe gestures for track navigation
setupSwipeGestures();
}
}
// Setup swipe gestures for track navigation
function setupSwipeGestures() {
const player = document.getElementById('enhancedGlobalPlayer');
if (!player) return;
let startX = 0;
let startY = 0;
let isSwipeGesture = false;
player.addEventListener('touchstart', function(e) {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
isSwipeGesture = false;
});
player.addEventListener('touchmove', function(e) {
if (e.touches.length === 1) {
const currentX = e.touches[0].clientX;
const currentY = e.touches[0].clientY;
const deltaX = Math.abs(currentX - startX);
const deltaY = Math.abs(currentY - startY);
// If horizontal movement is significant, mark as potential swipe
if (deltaX > 20 && deltaX > deltaY) {
isSwipeGesture = true;
}
}
});
player.addEventListener('touchend', function(e) {
if (!isSwipeGesture) return;
const endX = e.changedTouches[0].clientX;
const endY = e.changedTouches[0].clientY;
const deltaX = endX - startX;
const deltaY = endY - startY;
// Only detect horizontal swipes with sufficient distance
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 80) {
if (deltaX > 0) {
// Swipe right - previous track
console.log('π± Swipe right detected - previous track');
setTimeout(() => playPreviousTrack(), 100); // Small delay to avoid conflicts
} else {
// Swipe left - next track
console.log('π± Swipe left detected - next track');
setTimeout(() => playNextTrack(), 100); // Small delay to avoid conflicts
}
}
isSwipeGesture = false;
});
}
// Handle mobile touch start - simplified to avoid conflicts
function handleMobileTouchStart(e) {
// Only store touch start position for gesture detection
if (e.touches.length === 1) {
window.touchStartX = e.touches[0].clientX;
window.touchStartY = e.touches[0].clientY;
}
}
// Handle mobile touch end - simplified to avoid conflicts
function handleMobileTouchEnd(e) {
// Only detect swipe gestures if they're significant
if (window.touchStartX && window.touchStartY) {
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
const deltaX = touchEndX - window.touchStartX;
const deltaY = touchEndY - window.touchStartY;
// Minimum swipe distance - increased to avoid accidental triggers
const minSwipeDistance = 100;
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > minSwipeDistance) {
// Horizontal swipe detected
if (deltaX > 0) {
// Swipe right - previous track
console.log('π± Swipe right detected - previous track');
setTimeout(() => playPreviousTrack(), 100); // Small delay to avoid conflicts
} else {
// Swipe left - next track
console.log('π± Swipe left detected - next track');
setTimeout(() => playNextTrack(), 100); // Small delay to avoid conflicts
}
}
// Reset touch positions
window.touchStartX = null;
window.touchStartY = null;
}
}
// Playback speed control function
function cyclePlaybackSpeed() {
const speeds = [0.5, 0.75, 1, 1.25, 1.5, 2];
const currentIndex = speeds.indexOf(playbackRate);
playbackRate = speeds[(currentIndex + 1) % speeds.length];
const audio = document.getElementById('globalAudioElement');
if (audio) {
audio.playbackRate = playbackRate;
}
// Update speed button display if it exists
const speedBtn = document.getElementById('playerSpeedBtn');
if (speedBtn) {
const speedText = speedBtn.querySelector('.speed-text');
if (speedText) {
speedText.textContent = `${playbackRate}x`;
}
speedBtn.title = `Speed: ${playbackRate}x`;
}
console.log('π΅ Playback speed changed to:', playbackRate);
showNotification(`Playback speed: ${playbackRate}x`, 'info');
}
// Play track function - Main API for other pages - IMPROVED ERROR HANDLING
function playTrack(audioUrl, title, artist, duration = null, trackId = null) {
console.log('π΅ Global player playTrack called:', { audioUrl, title, artist, duration, trackId });
try {
// Show the player if it's hidden
const playerElement = document.getElementById('enhancedGlobalPlayer');
if (playerElement && playerElement.style.display === 'none') {
playerElement.style.display = 'flex';
}
// Fix parameter order if they're mixed up
if (typeof audioUrl === 'number' && typeof title === 'string' && title.startsWith('/audio_files/')) {
console.log('π Fixing parameter order - audioUrl and title were swapped');
const temp = audioUrl;
audioUrl = title;
title = artist;
artist = temp;
console.log('π Corrected parameters:', { audioUrl, title, artist, duration, trackId });
}
// Validate audio URL
if (!audioUrl) {
console.error('β No audio URL provided');
showNotification('Error: No audio URL provided', 'error');
return false;
}
// Clean up URL if needed (remove quotes, etc.)
audioUrl = audioUrl.replace(/^["']|["']$/g, '').trim();
// Check for invalid URLs
if (audioUrl === 'NULL' || audioUrl === 'null' || audioUrl === 'undefined' || audioUrl === '') {
console.error('β Invalid audio URL:', audioUrl);
showNotification('Error: Invalid audio URL', 'error');
return false;
}
// Get audio element
const audio = document.getElementById('globalAudioElement');
if (!audio) {
console.error('β Global audio element not found');
showNotification('Error: Player not initialized', 'error');
return false;
}
// Update track info
currentTrack = { audioUrl, title, artist, duration, id: trackId };
updateTrackDisplay(title, artist);
// Set audio source and load with error handling
try {
audio.src = audioUrl;
audio.load();
// Mobile-specific attributes
audio.playsInline = true;
audio.preload = 'metadata';
// Generate new waveform for this track
if (duration) {
generateInteractiveWaveform(duration);
} else {
generateInteractiveWaveform(180); // Default 3 minutes
}
// Play the track with enhanced error handling
const playPromise = audio.play();
if (playPromise !== undefined) {
playPromise.then(() => {
isPlaying = true;
updatePlayButton();
showNotification(`Now playing: ${title}`, 'success');
}).catch(error => {
console.error('β Audio play failed:', error);
// Mobile-specific error handling
if (error.name === 'NotAllowedError') {
console.log('π± User interaction required on mobile');
showNotification('Tap play again to start audio (mobile autoplay policy)', 'warning');
} else if (error.name === 'NotSupportedError') {
showNotification('Audio format not supported on this device', 'error');
} else if (error.name === 'AbortError') {
showNotification('Playback was aborted', 'warning');
} else if (error.message && error.message.includes('source')) {
showNotification('Audio source error - file may be missing', 'error');
} else {
showNotification('Playback error: ' + error.message, 'error');
}
isPlaying = false;
updatePlayButton();
});
}
// Return true since we successfully started the playback process
return true;
} catch (loadError) {
console.error('β Audio load failed:', loadError);
showNotification('Error loading audio: ' + loadError.message, 'error');
return false;
}
} catch (error) {
console.error('β PlayTrack failed:', error);
showNotification('Playback error: ' + error.message, 'error');
return false; // Return false on error
}
}
// Toggle play/pause
function togglePlayPause() {
console.log('π΅ Toggle play/pause called');
const audio = document.getElementById('globalAudioElement');
if (!audio) {
console.error('β Audio element not found');
return;
}
// Check if we have a track loaded
if (!currentTrack || !currentTrack.audioUrl) {
console.log('π΅ No track loaded, attempting to load playlist...');
// Try to load and play first track from current playlist
if (currentPlaylistData.length > 0) {
const firstTrack = currentPlaylistData[0];
console.log('π΅ Loading first track from playlist:', firstTrack.title);
playTrackFromPlaylist(firstTrack);
return;
} else {
console.log('π΅ No tracks available in playlist');
showNotification('No tracks available to play', 'error');
return;
}
}
if (isPlaying) {
console.log('βΈοΈ Pausing playback');
audio.pause();
isPlaying = false;
} else {
console.log('βΆοΈ Starting playback');
console.log('π΅ Current track:', currentTrack);
console.log('π΅ Audio ready state:', audio.readyState);
console.log('π΅ Audio src:', audio.src);
// Ensure audio is loaded and ready
if (audio.readyState < 2) { // HAVE_CURRENT_DATA
console.log('π΅ Audio not ready, loading...');
audio.load();
}
// Mobile-specific audio play handling
const playPromise = audio.play();
if (playPromise !== undefined) {
playPromise.then(() => {
isPlaying = true;
console.log('β
Playback started successfully');
showNotification('Now playing: ' + (currentTrack?.title || 'Track'), 'success');
}).catch(error => {
console.error('β Playback failed:', error);
isPlaying = false;
// Mobile-specific error handling
if (error.name === 'NotAllowedError') {
console.log('π± User interaction required on mobile');
showNotification('Tap the play button again to start playback', 'info');
} else if (error.name === 'NotSupportedError') {
console.log('π± Audio format not supported');
showNotification('Audio format not supported on this device', 'error');
} else {
showNotification('Playback failed: ' + error.message, 'error');
}
});
} else {
// Fallback for older browsers
isPlaying = true;
console.log('β
Playback started (fallback)');
}
}
updatePlayButton();
}
// Update play button state
function updatePlayButton() {
const playIcon = document.getElementById('playerPlayIcon');
if (playIcon) {
playIcon.className = isPlaying ? 'fas fa-pause' : 'fas fa-play';
}
}
// Update track display
function updateTrackDisplay(title, artist) {
const titleElement = document.getElementById('playerTrackTitle');
const artistElement = document.getElementById('playerTrackArtist');
if (titleElement) titleElement.textContent = title || 'Unknown Track';
if (artistElement) artistElement.textContent = artist || 'Unknown Artist';
}
// Update time displays
function updateTimeDisplay() {
const timeDisplay = document.getElementById('playerTimeDisplay');
if (timeDisplay) {
timeDisplay.textContent = formatTime(currentTime);
}
}
function updateDurationDisplay() {
const durationDisplay = document.getElementById('playerDurationDisplay');
if (durationDisplay) {
durationDisplay.textContent = formatTime(duration);
}
}
// Format time in MM:SS
function formatTime(seconds) {
if (!seconds || isNaN(seconds)) return '0:00';
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
// Update progress bar
function updateProgressBar() {
const progressBar = document.getElementById('playerProgressBar');
if (progressBar && duration > 0) {
const progress = (currentTime / duration) * 100;
progressBar.style.width = progress + '%';
}
}
// Seek to position
function seekToPosition(e) {
const audio = document.getElementById('globalAudioElement');
if (!audio) return;
const rect = e.currentTarget.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const width = rect.width;
const seekTime = (clickX / width) * duration;
audio.currentTime = seekTime;
}
// Update volume
function updateVolume() {
const audio = document.getElementById('globalAudioElement');
if (audio) {
audio.volume = volume;
}
// Update volume icon
const volumeIcon = document.getElementById('playerVolumeIcon');
const volumeBtn = document.getElementById('playerVolumeBtn');
if (volumeIcon) {
if (isMuted || volume === 0) {
volumeIcon.className = 'fas fa-volume-mute';
} else if (volume < 0.3) {
volumeIcon.className = 'fas fa-volume-down';
} else if (volume < 0.7) {
volumeIcon.className = 'fas fa-volume-down';
} else {
volumeIcon.className = 'fas fa-volume-up';
}
}
// Update volume button state
if (volumeBtn) {
if (isMuted || volume === 0) {
volumeBtn.classList.add('muted');
} else {
volumeBtn.classList.remove('muted');
}
}
// Update volume display
updateVolumeDisplay();
}
// Toggle volume slider visibility
function toggleVolumeSlider() {
const volumeContainer = document.getElementById('volumeSliderContainer');
if (volumeContainer) {
volumeContainer.classList.toggle('active');
console.log('π Volume slider toggled');
}
}
// Toggle mute
function toggleMute() {
if (isMuted) {
// Unmute - restore previous volume
volume = previousVolume;
isMuted = false;
console.log('π Unmuted, volume restored to:', volume);
} else {
// Mute - store current volume and set to 0
previousVolume = volume;
volume = 0;
isMuted = true;
console.log('π Muted, previous volume was:', previousVolume);
}
// Update UI
const volumeSlider = document.getElementById('playerVolumeSlider');
if (volumeSlider) {
volumeSlider.value = volume * 100;
}
updateVolume();
updateVolumeDisplay();
}
// Update volume display
function updateVolumeDisplay() {
const volumeDisplay = document.getElementById('volumeDisplay');
if (volumeDisplay) {
if (isMuted) {
volumeDisplay.textContent = 'Muted';
} else {
volumeDisplay.textContent = Math.round(volume * 100) + '%';
}
}
}
// Update play button state
function updatePlayButton() {
const playIcon = document.getElementById('playerPlayIcon');
if (playIcon) {
playIcon.className = isPlaying ? 'fas fa-pause' : 'fas fa-play';
}
}
// Update track display
function updateTrackDisplay(title, artist) {
const titleElement = document.getElementById('playerTrackTitle');
const artistElement = document.getElementById('playerTrackArtist');
if (titleElement) titleElement.textContent = title || 'Unknown Track';
if (artistElement) artistElement.textContent = artist || 'Unknown Artist';
}
// Update time displays
function updateTimeDisplay() {
const timeDisplay = document.getElementById('playerTimeDisplay');
if (timeDisplay) {
timeDisplay.textContent = formatTime(currentTime);
}
}
function updateDurationDisplay() {
const durationDisplay = document.getElementById('playerDurationDisplay');
if (durationDisplay) {
durationDisplay.textContent = formatTime(duration);
}
}
// Format time in MM:SS
function formatTime(seconds) {
if (!seconds || isNaN(seconds)) return '0:00';
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
// Update progress bar
function updateProgressBar() {
const progressBar = document.getElementById('playerProgressBar');
if (progressBar && duration > 0) {
const progress = (currentTime / duration) * 100;
progressBar.style.width = progress + '%';
}
}
// Seek to position
function seekToPosition(e) {
const audio = document.getElementById('globalAudioElement');
if (!audio) return;
const rect = e.currentTarget.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const width = rect.width;
const seekTime = (clickX / width) * duration;
audio.currentTime = seekTime;
}
// Toggle minimize
function toggleMinimize() {
const player = document.getElementById('enhancedGlobalPlayer');
if (player) {
isMinimized = !isMinimized;
player.classList.toggle('minimized', isMinimized);
const icon = document.getElementById('playerMinimizeBtn').querySelector('i');
if (icon) {
icon.className = isMinimized ? 'fas fa-chevron-up' : 'fas fa-chevron-down';
}
}
}
// Toggle auto-play
function toggleAutoPlay() {
autoPlay = !autoPlay;
const autoPlayBtn = document.getElementById('playerAutoPlayBtn');
if (autoPlayBtn) {
const icon = autoPlayBtn.querySelector('i');
if (icon) {
icon.className = autoPlay ? 'fas fa-play' : 'fas fa-pause';
}
autoPlayBtn.title = `Auto-play: ${autoPlay ? 'ON' : 'OFF'}`;
}
console.log('π΅ Auto-play toggled:', autoPlay);
showNotification(`Auto-play ${autoPlay ? 'enabled' : 'disabled'}`, 'info');
}
// Playlist functionality
let currentPlaylistData = [];
let currentTrackIndex = 0;
// Make playlist data globally accessible for click handlers
window.currentPlaylistData = currentPlaylistData;
function setupPlaylistFunctionality() {
const dropdown = document.getElementById('playlistDropdown');
if (dropdown) {
dropdown.value = currentPlaylist;
}
// Load initial playlist
loadPlaylist(currentPlaylist);
}
function switchPlaylist(playlistType) {
console.log('π΅ Switching to playlist:', playlistType);
currentPlaylist = playlistType;
currentTrackIndex = 0;
loadPlaylist(playlistType);
}
async function loadPlaylist(playlistType) {
console.log('π΅ Loading playlist:', playlistType);
try {
let apiUrl;
let playlistName;
switch (playlistType) {
case 'vip':
apiUrl = '/api/get_vip_sample_tracks.php?per_page=50';
playlistName = 'VIP Samples';
break;
case 'featured':
apiUrl = '/api/get_featured_tracks.php?per_page=50';
playlistName = 'Featured Tracks';
break;
case 'community':
apiUrl = '/api/get_community_tracks.php?per_page=50';
playlistName = 'Community Tracks';
break;
default:
apiUrl = '/api/get_vip_sample_tracks.php?per_page=50';
playlistName = 'VIP Samples';
}
const response = await fetch(apiUrl);
const data = await response.json();
if (data.success && data.tracks && data.tracks.length > 0) {
currentPlaylistData = data.tracks;
window.currentPlaylistData = data.tracks; // Update global reference
console.log(`β
Loaded ${data.tracks.length} tracks from ${playlistName}`);
console.log('π΅ First few tracks:', data.tracks.slice(0, 3).map(t => t.title));
// Update playlist info display
updatePlaylistInfo(playlistName, data.tracks.length);
// Auto-play first track if player is ready
if (window.globalPlayerReady && currentPlaylistData.length > 0) {
const firstTrack = currentPlaylistData[0];
updateTrackDisplay(firstTrack.title, firstTrack.artist_name);
console.log('π΅ First track loaded:', firstTrack.title);
}
} else {
console.warn('β οΈ No tracks found for playlist:', playlistType);
currentPlaylistData = [];
window.currentPlaylistData = []; // Update global reference
updatePlaylistInfo(playlistName, 0);
}
} catch (error) {
console.error('β Failed to load playlist:', error);
currentPlaylistData = [];
window.currentPlaylistData = []; // Update global reference
updatePlaylistInfo('Error', 0);
}
}
function updatePlaylistInfo(name, trackCount) {
const titleElement = document.getElementById('playerTrackTitle');
const artistElement = document.getElementById('playerTrackArtist');
if (titleElement) {
titleElement.textContent = trackCount > 0 ? `${name} (${trackCount} tracks)` : `${name} - No tracks available`;
}
if (artistElement) {
artistElement.textContent = trackCount > 0 ? 'Click play to start listening' : 'No tracks in this playlist';
}
}
function playNextTrack() {
if (currentPlaylistData.length === 0) {
console.log('π΅ No playlist loaded');
return;
}
currentTrackIndex = (currentTrackIndex + 1) % currentPlaylistData.length;
const track = currentPlaylistData[currentTrackIndex];
console.log('π΅ Playing next track:', track.title);
playTrackFromPlaylist(track);
}
function playPreviousTrack() {
if (currentPlaylistData.length === 0) {
console.log('π΅ No playlist loaded');
return;
}
currentTrackIndex = currentTrackIndex === 0 ? currentPlaylistData.length - 1 : currentTrackIndex - 1;
const track = currentPlaylistData[currentTrackIndex];
console.log('π΅ Playing previous track:', track.title);
playTrackFromPlaylist(track);
}
function playTrackFromPlaylist(track) {
if (!track) {
console.error('β Invalid track data: track is null or undefined');
showNotification('Error: Invalid track data', 'error');
return false;
}
if (!track.audio_url) {
console.error('β Invalid track data: missing audio_url', track);
showNotification('Error: Track is missing audio URL', 'error');
return false;
}
try {
console.log('π΅ Playing track from playlist:', track.title, track.audio_url);
// Show the player if it's hidden
const playerElement = document.getElementById('enhancedGlobalPlayer');
if (playerElement && playerElement.style.display === 'none') {
playerElement.style.display = 'flex';
}
// Clean up URL if needed (remove quotes, etc.)
const audioUrl = track.audio_url.replace(/^["']|["']$/g, '').trim();
// Check for invalid URLs
if (audioUrl === 'NULL' || audioUrl === 'null' || audioUrl === 'undefined' || audioUrl === '') {
console.error('β Invalid audio URL in track:', audioUrl);
showNotification('Error: Invalid audio URL in track', 'error');
return false;
}
// Set current track
currentTrack = {
audioUrl: audioUrl,
title: track.title || 'Unknown Track',
artist: track.artist_name || 'Unknown Artist',
duration: track.duration || 180,
id: track.id
};
// Update current track index
const trackIndex = currentPlaylistData.findIndex(t =>
t.audio_url === audioUrl ||
t.id === track.id ||
(t.title === track.title && t.artist_name === track.artist_name)
);
if (trackIndex !== -1) {
currentTrackIndex = trackIndex;
console.log('π΅ Track index in playlist:', currentTrackIndex);
} else {
console.log('π΅ Track not found in playlist, using current index:', currentTrackIndex);
}
// Update track display
updateTrackDisplay(currentTrack.title, currentTrack.artist);
// Generate waveform for this track
const trackDuration = currentTrack.duration;
generateInteractiveWaveform(trackDuration);
// Get the audio element
const audio = document.getElementById('globalAudioElement');
if (!audio) {
console.error('β Audio element not found');
showNotification('Error: Player not initialized', 'error');
return false;
}
try {
// Set the audio source and load
audio.src = audioUrl;
audio.load();
// Mobile-specific attributes
audio.playsInline = true;
audio.preload = 'metadata';
// Play the track with enhanced error handling
const playPromise = audio.play();
if (playPromise !== undefined) {
playPromise.then(() => {
isPlaying = true;
updatePlayButton();
showNotification(`Now playing: ${currentTrack.title}`, 'success');
console.log('β
Track started playing successfully');
return true;
}).catch(error => {
console.error('β Audio play failed:', error);
// Mobile-specific error handling
if (error.name === 'NotAllowedError') {
console.log('π± User interaction required on mobile');
showNotification('Tap play again to start audio (mobile autoplay policy)', 'warning');
} else if (error.name === 'NotSupportedError') {
showNotification('Audio format not supported on this device', 'error');
} else if (error.name === 'AbortError') {
showNotification('Playback was aborted', 'warning');
} else if (error.message && error.message.includes('source')) {
showNotification('Audio source error - file may be missing', 'error');
} else {
showNotification('Playback error: ' + error.message, 'error');
}
isPlaying = false;
updatePlayButton();
return false;
});
} else {
// Fallback for older browsers
isPlaying = true;
updatePlayButton();
console.log('β
Playback started (fallback)');
return true;
}
// Return true since we successfully started the playback process
return true;
} catch (loadError) {
console.error('β Audio load failed:', loadError);
showNotification('Error loading audio: ' + loadError.message, 'error');
return false;
}
} catch (error) {
console.error('β Failed to play track from playlist:', error);
showNotification('Playback error: ' + error.message, 'error');
return false;
}
}
function stopPlayback() {
const audio = document.getElementById('globalAudioElement');
if (audio) {
audio.pause();
audio.currentTime = 0;
}
isPlaying = false;
updatePlayButton();
}
// Show notification
function showNotification(message, type = 'info') {
console.log(`π’ [${type.toUpperCase()}] ${message}`);
// Create notification element
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
// Style the notification
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${type === 'error' ? '#e74c3c' : type === 'success' ? '#27ae60' : type === 'warning' ? '#f39c12' : '#3498db'};
color: white;
padding: 1rem 1.5rem;
border-radius: 8px;
z-index: 10001;
font-size: 1.4rem;
font-weight: 500;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
transform: translateX(100%);
transition: transform 0.3s ease;
max-width: 300px;
word-wrap: break-word;
`;
document.body.appendChild(notification);
// Animate in
setTimeout(() => {
notification.style.transform = 'translateX(0)';
}, 100);
// Auto-remove after 4 seconds
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (document.body.contains(notification)) {
document.body.removeChild(notification);
}
}, 300);
}, 4000);
}
// Wait for global player to be ready
function waitForGlobalPlayer(callback, maxAttempts = 50) {
let attempts = 0;
const checkReady = () => {
attempts++;
if (window.globalPlayerReady && window.enhancedGlobalPlayer) {
console.log('π΅ Global player is ready, executing callback');
callback();
return;
}
if (attempts >= maxAttempts) {
console.log('π΅ waitForGlobalPlayer: Max attempts reached, player not ready');
return;
}
// Check again in 100ms
setTimeout(checkReady, 100);
};
checkReady();
}
// Make waitForGlobalPlayer globally available
window.waitForGlobalPlayer = waitForGlobalPlayer;
// Mobile-specific test function
function testMobilePlayback() {
console.log('π± Testing mobile playback...');
const audio = document.getElementById('globalAudioElement');
if (!audio) {
console.error('β Audio element not found');
return false;
}
// Test audio capabilities
console.log('π± Audio element properties:');
console.log('- readyState:', audio.readyState);
console.log('- networkState:', audio.networkState);
console.log('- paused:', audio.paused);
console.log('- ended:', audio.ended);
console.log('- src:', audio.src);
console.log('- volume:', audio.volume);
console.log('- muted:', audio.muted);
// Test if we can play audio
if (audio.readyState >= 2) {
console.log('β
Audio is ready to play');
return true;
} else {
console.log('β οΈ Audio not ready, readyState:', audio.readyState);
return false;
}
}
// Add mobile test to global API
window.testMobilePlayer = testMobilePlayback;
// Public API
window.enhancedGlobalPlayer = {
playTrack: playTrack,
togglePlayPause: togglePlayPause,
playNextTrack: playNextTrack,
playPreviousTrack: playPreviousTrack,
stopPlayback: stopPlayback,
setVolume: (vol) => { volume = vol; updateVolume(); },
setPlaybackRate: (rate) => { playbackRate = rate; },
getCurrentTrack: () => currentTrack,
isPlaying: () => isPlaying,
initialize: initializeGlobalPlayer,
init: initializeGlobalPlayer, // Alias for compatibility
loadPlaylist: loadPlaylist,
getCurrentPlaylist: () => currentPlaylist,
getPlaylistTrackCount: () => currentPlaylistData.length,
showPlayer: () => {
const player = document.getElementById('enhancedGlobalPlayer');
if (player) {
player.style.display = 'flex';
console.log('π΅ Global player shown');
}
},
hidePlayer: () => {
const player = document.getElementById('enhancedGlobalPlayer');
if (player) {
player.style.display = 'none';
console.log('π΅ Global player hidden');
}
},
playRandomTrack: () => {
console.log('π² Random track button clicked!');
console.log('π΅ Current playlist data:', currentPlaylistData);
console.log('π΅ Playlist length:', currentPlaylistData.length);
// Show the player if it's hidden
const playerElement = document.getElementById('enhancedGlobalPlayer');
if (playerElement && playerElement.style.display === 'none') {
playerElement.style.display = 'flex';
}
if (currentPlaylistData && currentPlaylistData.length > 0) {
// Make sure we have valid tracks in the playlist
const validTracks = currentPlaylistData.filter(track =>
track && track.audio_url &&
track.audio_url !== 'null' &&
track.audio_url !== 'NULL' &&
track.audio_url !== ''
);
if (validTracks.length > 0) {
// Select a random track from valid tracks
const randomIndex = Math.floor(Math.random() * validTracks.length);
const track = validTracks[randomIndex];
console.log('π² Selected random track:', track.title);
// Find this track in the main playlist
const mainPlaylistIndex = currentPlaylistData.findIndex(t =>
t.id === track.id ||
t.audio_url === track.audio_url ||
(t.title === track.title && t.artist_name === track.artist_name)
);
if (mainPlaylistIndex !== -1) {
currentTrackIndex = mainPlaylistIndex;
}
// Play the selected track
playTrackFromPlaylist(track);
return true;
} else {
console.warn('β οΈ No valid tracks in playlist for random selection');
showNotification('No valid tracks available for random play', 'warning');
// Try to load the playlist again
loadPlaylist(currentPlaylist);
return false;
}
} else {
console.warn('β οΈ No tracks in playlist for random selection');
showNotification('No tracks available for random play', 'warning');
// Try to load the playlist
loadPlaylist('vip');
return false;
}
}
};
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeGlobalPlayer);
} else {
initializeGlobalPlayer();
}
// Add waitForGlobalPlayer function for other pages to use
window.waitForGlobalPlayer = function(callback, maxAttempts = 20) {
if (window.globalPlayerReady && window.enhancedGlobalPlayer) {
callback();
} else if (maxAttempts > 0) {
setTimeout(() => waitForGlobalPlayer(callback, maxAttempts - 1), 250);
} else {
console.error('π΅ waitForGlobalPlayer: Max attempts reached, player not ready');
}
};
// Ensure global player is accessible
console.log('π΅ Global player setup complete');
console.log('π΅ enhancedGlobalPlayer available:', typeof window.enhancedGlobalPlayer !== 'undefined');
console.log('π΅ globalPlayerReady:', window.globalPlayerReady);
// Verify all required functions exist
const requiredFunctions = [
'playTrack', 'togglePlayPause', 'playNextTrack', 'playPreviousTrack',
'stopPlayback', 'updateVolume', 'updatePlayButton', 'updateTrackDisplay'
];
requiredFunctions.forEach(funcName => {
if (typeof window[funcName] === 'undefined' && typeof eval(funcName) === 'undefined') {
console.warn(`β οΈ Required function ${funcName} not found`);
} else {
console.log(`β
Function ${funcName} available`);
}
});
// Ensure player is visible after initialization
setTimeout(() => {
const player = document.getElementById('enhancedGlobalPlayer');
if (player) {
player.style.display = 'flex';
console.log('π΅ Global player visibility enforced');
}
// Auto-load VIP playlist after player is visible
if (window.globalPlayerReady) {
loadPlaylist('vip');
}
}, 100);
// Listen for AJAX navigation events
document.addEventListener('pageContentUpdated', () => {
console.log('π΅ Global player: Page content updated, reinitializing if needed');
if (!window.globalPlayerReady) {
initializeGlobalPlayer();
}
});
// Waveform Generation Functions
function generateWaveform() {
console.log('π Generating dynamic waveform visualizer');
// Generate dynamic background waveform (decorative)
const barsContainer = document.getElementById('playerWaveformBars');
if (!barsContainer) {
console.warn('β οΈ Waveform bars container not found');
return;
}
const numberOfBars = 60; // More bars for better visual effect
let barsHtml = '';
for (let i = 0; i < numberOfBars; i++) {
// Create much taller, more visible wave pattern
const baseHeight = Math.sin((i / numberOfBars) * Math.PI * 3) * 60 + 70; // 10% to 130%
const randomVariation = (Math.random() - 0.5) * 40; // Add some randomness
const height = Math.max(25, Math.min(120, baseHeight + randomVariation));
// Staggered animation delays for wave effect
const animationDelay = (i / numberOfBars) * 2; // 0 to 2 seconds
barsHtml += `<div class="waveform-bar" style="height: ${height}%; animation-delay: ${animationDelay}s;"></div>`;
}
barsContainer.innerHTML = barsHtml;
console.log('π Dynamic waveform visualizer generated');
}
function generateInteractiveWaveform(duration = 180) {
console.log('π Generating interactive waveform for duration:', duration);
const progressBarsContainer = document.getElementById('waveformProgressBars');
if (!progressBarsContainer) {
console.warn('β οΈ Waveform progress bars container not found');
return;
}
// Create bars based on actual song duration
const numberOfBars = Math.min(150, Math.floor(duration / 1.2)); // 1 bar per 1.2 seconds
let barsHtml = '';
// Generate simple, visible bars
for (let i = 0; i < numberOfBars; i++) {
const percentage = (i / numberOfBars) * 100;
// Make bars much taller and more visible
const height = Math.max(30, Math.random() * 60 + 20); // 30-90% height
barsHtml += `<div class="waveform-progress-bar"
style="height: ${height}%;"
data-position="${percentage}"
onclick="seekToWaveformPosition(${percentage})">
</div>`;
}
progressBarsContainer.innerHTML = barsHtml;
// Store waveform data for updates
window.currentWaveformBars = numberOfBars;
window.currentWaveformDuration = duration;
console.log('π Interactive waveform generated:', numberOfBars, 'bars for', duration, 'seconds');
}
function seekToWaveformPosition(percentage) {
const audio = document.getElementById('globalAudioElement');
if (audio && duration > 0) {
const seekTime = (percentage / 100) * duration;
audio.currentTime = seekTime;
console.log('π Seeking to waveform position:', percentage + '%', '(', seekTime, 'seconds)');
}
}
function updateWaveformProgress(currentTime, duration) {
if (!window.currentWaveformBars || !duration) return;
const progressBars = document.querySelectorAll('.waveform-progress-bar');
const currentBar = Math.floor((currentTime / duration) * window.currentWaveformBars);
progressBars.forEach((bar, index) => {
if (index < currentBar) {
// Already played - show as completed
bar.classList.add('played');
bar.classList.remove('playing');
bar.style.opacity = '0.8';
bar.style.transform = 'scaleY(1)';
} else if (index === currentBar) {
// Currently playing - highlight and animate
bar.classList.add('playing');
bar.classList.remove('played');
bar.style.opacity = '1';
bar.style.transform = 'scaleY(1.3)';
bar.style.boxShadow = '0 0 12px rgba(79, 172, 254, 0.8)';
} else {
// Not yet played - show as upcoming
bar.classList.remove('played', 'playing');
bar.style.opacity = '0.4';
bar.style.transform = 'scaleY(0.8)';
bar.style.boxShadow = 'none';
}
});
}
// Initialize waveform when player is ready
setTimeout(() => {
generateWaveform();
generateInteractiveWaveform(180); // Default 3 minutes
}, 500);
// Additional safety check - ensure global player is exposed
setTimeout(() => {
if (typeof window.enhancedGlobalPlayer === 'undefined') {
console.error('β Global player still not exposed after timeout, forcing exposure');
window.enhancedGlobalPlayer = {
playTrack: playTrack,
togglePlayPause: togglePlayPause,
playNextTrack: playNextTrack,
playPreviousTrack: playPreviousTrack,
stopPlayback: stopPlayback,
setVolume: (vol) => { volume = vol; updateVolume(); },
setPlaybackRate: (rate) => { playbackRate = rate; },
getCurrentTrack: () => currentTrack,
isPlaying: () => isPlaying,
initialize: initializeGlobalPlayer,
init: initializeGlobalPlayer,
loadPlaylist: loadPlaylist,
getCurrentPlaylist: () => currentPlaylist,
getPlaylistTrackCount: () => currentPlaylistData.length,
showPlayer: () => {
const player = document.getElementById('enhancedGlobalPlayer');
if (player) {
player.style.display = 'flex';
console.log('π΅ Global player shown');
}
},
hidePlayer: () => {
const player = document.getElementById('enhancedGlobalPlayer');
if (player) {
player.style.display = 'none';
console.log('π΅ Global player hidden');
}
},
playRandomTrack: () => {
console.log('π² Random track button clicked!');
if (currentPlaylistData && currentPlaylistData.length > 0) {
const randomIndex = Math.floor(Math.random() * currentPlaylistData.length);
const track = currentPlaylistData[randomIndex];
playTrackFromPlaylist(track);
}
}
};
console.log('β
Global player forced exposure completed');
}
}, 1000);
console.log('π΅ Enhanced Global Player script loaded');
})();
</script>