![]() 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/-109ca8d5/ |
<?php
// Global Music Player - Persistent Across AJAX Navigation
// This player will be included in the main layout and stay active during page navigation
// Prevent multiple includes
if (defined('GLOBAL_PLAYER_INCLUDED')) {
return;
}
define('GLOBAL_PLAYER_INCLUDED', true);
// Use external CDN tracks only - no database queries
$lastCommunitySong = null;
?>
<!--
================================================================================
GLOBAL MUSIC PLAYER - COMPLETE DOCUMENTATION
================================================================================
ARCHITECTURE:
- Fixed position player at bottom of page (z-index: 9999)
- Persists across AJAX navigation without reloading
- Single audio element managed by globalPlayer object
- Playlist loaded from database metadata + CDN audio files
INTEGRATION ON NEW PAGES:
1. Include this file: <?php include 'includes/global_player.php'; ?>
2. For AJAX pages, ensure it's included even when $is_ajax = true
3. Use window.playTrackWithGlobalPlayer(audioUrl, title, artist) to play tracks
PLAYER CONTROLS:
- Play/Pause: Toggles between play/pause states
- Stop: Stops and resets to beginning
- Previous/Next: Navigate playlist
- Volume: Adjusts audio volume
- Progress Bar: Shows playback progress, clickable to seek
- Playlist Toggle: Opens/closes playlist panel
EVENT HANDLING:
- All buttons use addEventListener with stopImmediatePropagation() to prevent AJAX interference
- this context preserved using const self = this pattern
- Audio events: loadeddata, canplaythrough, timeupdate, ended, error
PLAYLIST SOURCES:
1. Primary: Database tracks via /api/get_community_tracks.php
- Filters: status='complete' AND audio_url LIKE '%apiboxfiles.erweima.ai%'
- Sorts: created_at DESC (newest first)
- Limits: 50 tracks
2. Fallback: Hardcoded default track if no database tracks
AUDIO MANAGEMENT:
- Single audio element (this.audio) created per track
- Auto-play logic based on wasPlaying state
- Volume persisted in localStorage
- Error handling with fallback to next track
STATE MANAGEMENT:
- isPlaying: Current play/pause state
- wasPlaying: Previous state (for auto-play decisions)
- currentTrackIndex: Position in playlist
- initialized: Prevents multiple initializations
GLOBAL FUNCTIONS:
- window.playTrackWithGlobalPlayer(audioUrl, title, artist): Play track from any page
- window.ensureGlobalPlayer(): Ensure player is ready
- window.globalPlayer: Direct access to player object
AJAX COMPATIBILITY:
- Player persists during AJAX navigation
- Event listeners use stopImmediatePropagation() to prevent interference
- Player re-initializes if needed on new page loads
DEBUGGING:
- Extensive console logging with π΅ prefix
- window.debugGlobalPlayer() function available
- Error handling with fallback mechanisms
================================================================================
-->
<!-- Global Music Player -->
<div id="globalMusicPlayer" class="global-music-player">
<div class="player-container">
<!-- Track Info -->
<div class="track-info">
<div class="track-details">
<div class="track-title" id="globalTrackTitle">
Community Music Ready
</div>
<div class="track-artist" id="globalTrackArtist">
<span id="globalTrackArtistName">Loading community tracks...</span>
<span id="globalTrackCount" style="display: none; color: #48bb78; font-size: 0.8em;"></span>
<a href="#" id="globalTrackArtistLink" class="artist-link" style="display: none;">
<i class="fas fa-user"></i> View Profile
</a>
</div>
</div>
</div>
<!-- Controls -->
<div class="player-controls">
<button class="control-btn" id="globalPrevBtn">
<i class="fas fa-step-backward"></i>
</button>
<button class="control-btn play-btn" id="globalPlayBtn">
<i class="fas fa-play" id="globalPlayIcon"></i>
</button>
<button class="control-btn" id="globalStopBtn">
<i class="fas fa-stop"></i>
</button>
<button class="control-btn" id="globalNextBtn">
<i class="fas fa-step-forward"></i>
</button>
</div>
<!-- Progress Bar -->
<div class="progress-container">
<div class="progress-bar" id="globalProgressBar">
<div class="progress-fill" id="globalProgressFill"></div>
</div>
<div class="time-display">
<span id="globalCurrentTime">0:00</span>
<span id="globalTotalTime">0:00</span>
</div>
</div>
<!-- Volume Control -->
<div class="volume-control">
<i class="fas fa-volume-up" id="globalVolumeIcon"></i>
<input type="range" id="globalVolumeSlider" min="0" max="100" value="50" class="volume-slider">
</div>
<!-- Playlist Toggle -->
<button class="playlist-toggle" id="globalPlaylistToggle">
<i class="fas fa-list"></i>
</button>
</div>
<!-- Playlist Panel -->
<div class="playlist-panel" id="globalPlaylistPanel">
<div class="playlist-header">
<h3>Community Playlist</h3>
<button class="close-playlist" id="globalClosePlaylist">
<i class="fas fa-times"></i>
</button>
</div>
<div class="playlist-tracks" id="globalPlaylistTracks">
<div class="playlist-loading" id="playlistLoading">
<i class="fas fa-spinner fa-spin"></i>
<span>Loading community tracks...</span>
</div>
<!-- Playlist tracks will be loaded here -->
</div>
</div>
</div>
<style>
/* Global Music Player Styles */
.global-music-player {
position: fixed !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
background: rgba(10, 10, 10, 0.95) !important;
backdrop-filter: blur(20px) !important;
border-top: 1px solid rgba(255, 255, 255, 0.1) !important;
z-index: 9999 !important;
padding: 1rem 2rem !important;
display: flex !important;
align-items: center !important;
justify-content: space-between !important;
min-height: 80px !important;
transform: translateY(0) !important;
transition: transform 0.3s ease !important;
/* ALWAYS VISIBLE - NO EXCEPTIONS */
opacity: 1 !important;
visibility: visible !important;
/* FORCE VISIBILITY - OVERRIDE ANY HIDDEN STATES */
display: flex !important;
max-height: none !important;
overflow: visible !important;
clip: auto !important;
clip-path: none !important;
}
.global-music-player.active {
transform: translateY(0);
opacity: 1;
visibility: visible;
}
.player-container {
display: flex;
align-items: center;
gap: 2rem;
flex: 1;
max-width: 1200px;
margin: 0 auto;
}
.track-info {
flex: 1;
min-width: 200px;
}
.track-details {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.track-title {
font-size: 1.4rem;
font-weight: 600;
color: white;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.track-artist {
font-size: 1.2rem;
color: #a0aec0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: flex;
align-items: center;
gap: 1rem;
}
.artist-link {
color: #667eea;
text-decoration: none;
font-size: 1rem;
padding: 0.3rem 0.8rem;
border-radius: 12px;
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.2);
transition: all 0.3s ease;
white-space: nowrap;
}
.artist-link:hover {
background: rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.4);
transform: translateY(-1px);
}
.player-controls {
display: flex;
align-items: center;
gap: 1rem;
}
.control-btn {
background: rgba(255, 255, 255, 0.1);
border: none;
color: white;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.control-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: scale(1.1);
}
.play-btn {
width: 50px;
height: 50px;
background: linear-gradient(135deg, #667eea, #764ba2);
}
.play-btn:hover {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
}
.progress-container {
flex: 2;
display: flex;
flex-direction: column;
gap: 0.5rem;
min-width: 300px;
}
.progress-bar {
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
cursor: pointer;
position: relative;
}
.progress-fill {
height: 100%;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 2px;
width: 0%;
transition: width 0.1s ease;
}
.time-display {
display: flex;
justify-content: space-between;
font-size: 1.2rem;
color: #a0aec0;
}
.volume-control {
display: flex;
align-items: center;
gap: 1rem;
min-width: 150px;
}
.volume-control i {
color: #a0aec0;
font-size: 1.4rem;
}
.volume-slider {
flex: 1;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
outline: none;
cursor: pointer;
}
.volume-slider::-webkit-slider-thumb {
appearance: none;
width: 12px;
height: 12px;
background: #667eea;
border-radius: 50%;
cursor: pointer;
}
.playlist-toggle {
background: rgba(255, 255, 255, 0.1);
border: none;
color: white;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.playlist-toggle:hover {
background: rgba(255, 255, 255, 0.2);
}
.playlist-panel {
position: fixed;
bottom: 80px;
right: 2rem;
width: 350px;
max-height: 400px;
background: rgba(10, 10, 10, 0.95);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
display: none;
flex-direction: column;
z-index: 10000;
}
.playlist-panel.active {
display: flex;
}
.playlist-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.playlist-header h3 {
color: white;
font-size: 1.6rem;
font-weight: 600;
}
.close-playlist {
background: none;
border: none;
color: #a0aec0;
cursor: pointer;
font-size: 1.4rem;
}
.playlist-tracks {
flex: 1;
overflow-y: auto;
padding: 1rem;
}
.playlist-track {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 0.5rem;
}
.playlist-track:hover {
background: rgba(255, 255, 255, 0.1);
}
.playlist-track.active {
background: rgba(102, 126, 234, 0.2);
border-left: 3px solid #667eea;
}
.playlist-track-info {
flex: 1;
min-width: 0;
}
.playlist-track-title {
font-size: 1.3rem;
color: white;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.playlist-track-artist {
font-size: 1.1rem;
color: #a0aec0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.playlist-track-actions {
display: flex;
align-items: center;
gap: 0.5rem;
}
.playlist-play-btn {
background: rgba(102, 126, 234, 0.2);
border: 1px solid rgba(102, 126, 234, 0.3);
color: #667eea;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
font-size: 1rem;
}
.playlist-play-btn:hover {
background: rgba(102, 126, 234, 0.4);
border-color: rgba(102, 126, 234, 0.6);
transform: scale(1.1);
}
.playlist-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
padding: 2rem;
color: #a0aec0;
font-size: 1.4rem;
flex-direction: column;
}
.playlist-loading i {
font-size: 1.6rem;
}
/* Mobile Responsive */
@media (max-width: 768px) {
.global-music-player {
padding: 0.8rem 1rem;
min-height: 60px;
}
.player-container {
gap: 0.8rem;
flex-wrap: wrap;
}
.track-info {
min-width: 100px;
flex: 1;
min-width: 0;
}
.track-title {
font-size: 1.2rem;
}
.track-artist {
font-size: 1rem;
}
.player-controls {
gap: 0.5rem;
flex-shrink: 0;
}
.control-btn {
width: 35px;
height: 35px;
font-size: 1rem;
}
.play-btn {
width: 40px;
height: 40px;
}
.progress-container {
min-width: 120px;
flex: 1;
min-width: 0;
}
.progress-bar {
height: 4px;
}
.time-display {
font-size: 1rem;
}
.volume-control {
min-width: 80px;
flex-shrink: 0;
}
.volume-slider {
width: 60px;
}
.playlist-toggle {
width: 35px;
height: 35px;
font-size: 1rem;
}
.playlist-panel {
width: calc(100vw - 2rem);
right: 1rem;
left: 1rem;
bottom: 80px;
max-height: 400px;
}
.playlist-header {
padding: 1.5rem;
}
.playlist-header h3 {
font-size: 1.6rem;
}
.playlist-content {
max-height: 300px;
padding: 1rem;
}
.playlist-track {
padding: 1rem;
margin-bottom: 0.5rem;
}
.playlist-track-title {
font-size: 1.3rem;
}
.playlist-track-artist {
font-size: 1.1rem;
}
}
@media (max-width: 480px) {
.global-music-player {
padding: 0.6rem 0.8rem;
min-height: 55px;
}
.player-container {
gap: 0.5rem;
}
.track-title {
font-size: 1.1rem;
}
.track-artist {
font-size: 0.9rem;
}
.control-btn {
width: 30px;
height: 30px;
font-size: 0.9rem;
}
.play-btn {
width: 35px;
height: 35px;
}
.progress-container {
min-width: 100px;
}
.time-display {
font-size: 0.9rem;
}
.volume-control {
min-width: 60px;
}
.volume-slider {
width: 50px;
}
.playlist-toggle {
width: 30px;
height: 30px;
font-size: 0.9rem;
}
.playlist-panel {
width: calc(100vw - 1rem);
right: 0.5rem;
left: 0.5rem;
bottom: 70px;
}
}
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.close-dj:hover {
background: rgba(245, 101, 101, 0.3);
}
</style>
<script>
// Global Music Player JavaScript
const globalPlayer = {
audio: null,
playlist: [],
currentTrackIndex: -1,
isPlaying: false,
wasPlaying: false,
volume: localStorage.getItem('globalPlayerVolume') ? parseFloat(localStorage.getItem('globalPlayerVolume')) : 0.5,
initialized: false,
initializationPromise: null,
init() {
// Prevent multiple initializations
if (this.initialized) {
console.log('π΅ Global player already initialized');
return Promise.resolve();
}
if (this.initializationPromise) {
console.log('π΅ Global player initialization already in progress');
return this.initializationPromise;
}
this.initializationPromise = new Promise(async (resolve, reject) => {
console.log('π΅ Global player initializing...');
try {
this.setupEventListeners();
// Show player immediately
this.showPlayer();
// Load community playlist
console.log('π΅ Loading community playlist...');
try {
await this.loadCommunityPlaylist();
console.log('π΅ Community playlist loaded successfully');
} catch (error) {
console.error('π΅ Failed to load community playlist:', error);
// Continue anyway - player can still play individual tracks
}
this.initialized = true;
console.log('π΅ Global player initialization complete');
resolve();
// Debug: Log initial state
console.log('π΅ Initial player state:', {
playlistLength: this.playlist.length,
currentTrackIndex: this.currentTrackIndex,
isPlaying: this.isPlaying
});
} catch (error) {
console.error('π΅ Error during global player initialization:', error);
reject(error);
}
});
return this.initializationPromise;
},
setupEventListeners() {
console.log('π΅ Setting up event listeners...');
// Play button - FIXED VERSION
const playBtn = document.getElementById('globalPlayBtn');
console.log('π΅ Play button element:', !!playBtn);
if (playBtn) {
console.log('π΅ Adding click listener to play button');
// Clear any existing onclick handlers
playBtn.onclick = null;
// Add proper play/pause functionality - FIXED WITH BINDING
const self = this;
playBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
console.log('π΅ Play button clicked!');
// This click counts as user interaction for audio
if (self.audio && self.audio.src) {
// Try to unlock audio context first
self.audio.play().then(() => {
console.log('π΅ Audio unlocked on play button click');
self.audio.pause(); // Pause immediately
// Now toggle play/pause
self.togglePlayPause();
}).catch(error => {
console.log('π΅ Audio unlock failed, trying toggle anyway:', error);
self.togglePlayPause();
});
} else {
self.togglePlayPause();
}
});
// Also add onclick as backup
playBtn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
console.log('π΅ Play button onclick fired!');
self.togglePlayPause();
};
console.log('π΅ Play button event listener added successfully');
} else {
console.error('π΅ Play button not found!');
}
// Previous button
const prevBtn = document.getElementById('globalPrevBtn');
if (prevBtn) {
// Clear any existing onclick handlers
prevBtn.onclick = null;
// Add event listener directly without cloning - FIXED WITH BINDING
const self = this;
prevBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('π΅ Previous button clicked');
self.previousTrack();
});
console.log('π΅ Previous button event listener added successfully');
} else {
console.error('π΅ Previous button not found!');
}
// Stop button - FIXED VERSION
const stopBtn = document.getElementById('globalStopBtn');
if (stopBtn) {
// Clear any existing onclick handlers
stopBtn.onclick = null;
// Add proper stop functionality - FIXED WITH BINDING
const self = this;
stopBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('π΅ Stop button clicked!');
self.stopTrack();
});
// Also add onclick as backup
stopBtn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
console.log('π΅ Stop button onclick fired!');
self.stopTrack();
};
console.log('π΅ Stop button event listener added successfully');
} else {
console.error('π΅ Stop button not found!');
}
// Next button
const nextBtn = document.getElementById('globalNextBtn');
if (nextBtn) {
// Clear any existing onclick handlers
nextBtn.onclick = null;
// Add event listener directly without cloning - FIXED WITH BINDING
const self = this;
nextBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('π΅ Next button clicked');
self.nextTrack();
});
console.log('π΅ Next button event listener added successfully');
} else {
console.error('π΅ Next button not found!');
}
// Artist profile link
const artistLink = document.getElementById('globalTrackArtistLink');
if (artistLink) {
// Clear any existing onclick handlers
artistLink.onclick = null;
// Add event listener for AJAX navigation
const self = this;
artistLink.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('π΅ Artist profile link clicked');
const href = this.getAttribute('href');
if (href && href !== '#') {
// Extract artist ID from href
const urlParams = new URLSearchParams(href.split('?')[1]);
const artistId = urlParams.get('id');
console.log('π΅ Artist ID extracted:', artistId);
// Use the site's AJAX navigation system
if (typeof window.navigateToArtistProfile === 'function') {
console.log('π΅ Using AJAX navigation for artist profile');
window.navigateToArtistProfile(artistId);
} else if (typeof window.loadPage === 'function') {
console.log('π΅ Using fallback loadPage navigation');
window.loadPage(href);
} else {
console.log('π΅ Using regular navigation');
window.location.href = href;
}
}
});
console.log('π΅ Artist profile link event listener added successfully');
} else {
console.error('π΅ Artist profile link not found!');
}
// Volume slider - FIXED VERSION WITH PROPER BINDING
const volumeSlider = document.getElementById('globalVolumeSlider');
if (volumeSlider) {
console.log('π΅ Setting up volume slider...');
// Clear any existing event listeners
volumeSlider.oninput = null;
volumeSlider.onchange = null;
volumeSlider.onclick = null;
// Add event listener directly - FIXED WITH PROPER BINDING
const self = this;
// Input event for real-time volume changes
volumeSlider.addEventListener('input', function(e) {
e.preventDefault();
e.stopPropagation();
const newVolume = e.target.value / 100;
self.volume = newVolume;
console.log('π΅ Volume changed to:', self.volume, '(', e.target.value, '%)');
if (self.audio) {
self.audio.volume = self.volume;
console.log('π΅ Applied volume to audio element:', self.audio.volume);
} else {
console.log('π΅ No audio element to apply volume to');
}
// Update volume icon
self.updateVolumeIcon();
// Store volume preference
localStorage.setItem('globalPlayerVolume', self.volume);
});
// Change event for final volume setting
volumeSlider.addEventListener('change', function(e) {
e.preventDefault();
e.stopPropagation();
const newVolume = e.target.value / 100;
self.volume = newVolume;
console.log('π΅ Volume finalized to:', self.volume, '(', e.target.value, '%)');
if (self.audio) {
self.audio.volume = self.volume;
}
self.updateVolumeIcon();
localStorage.setItem('globalPlayerVolume', self.volume);
});
// Click handler for volume slider - FIXED WITH BINDING
volumeSlider.addEventListener('click', function(e) {
e.stopPropagation();
console.log('π΅ Volume slider clicked');
});
// Set initial volume
volumeSlider.value = this.volume * 100;
console.log('π΅ Initialized volume slider to:', this.volume * 100, '%');
this.updateVolumeIcon();
console.log('π΅ Volume slider event listeners added successfully');
} else {
console.error('π΅ Volume slider element not found!');
}
// Playlist toggle - FIXED VERSION
const playlistToggle = document.getElementById('globalPlaylistToggle');
if (playlistToggle) {
// Clear any existing onclick handlers
playlistToggle.onclick = null;
// Add click event listener - FIXED WITH BINDING
const self = this;
playlistToggle.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('π΅ Playlist toggle clicked');
self.togglePlaylist();
});
console.log('π΅ Playlist toggle event listener added successfully');
} else {
console.error('π΅ Playlist toggle not found!');
}
// Close playlist - FIXED WITH BINDING
const closePlaylist = document.getElementById('globalClosePlaylist');
if (closePlaylist) {
const self = this;
closePlaylist.addEventListener('click', function() {
console.log('π΅ Close playlist clicked');
self.togglePlaylist();
});
} else {
console.error('π΅ Close playlist button not found!');
}
// Progress bar click - FIXED VERSION
const progressBar = document.getElementById('globalProgressBar');
if (progressBar) {
// Clear any existing onclick handlers
progressBar.onclick = null;
// Add event listener directly - FIXED WITH BINDING
const self = this;
progressBar.addEventListener('click', function(e) {
console.log('π΅ Progress bar clicked');
if (self.audio && self.audio.duration) {
const rect = e.target.getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
const newTime = percent * self.audio.duration;
console.log('π΅ Seeking to:', newTime, 'seconds (', percent * 100, '%)');
self.audio.currentTime = newTime;
} else {
console.log('π΅ No audio or duration available for seeking');
}
});
console.log('π΅ Progress bar event listener added successfully');
} else {
console.error('π΅ Progress bar not found!');
}
// Keyboard shortcuts - FIXED WITH BINDING
const self = this;
document.addEventListener('keydown', function(e) {
if (e.code === 'Space' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
e.preventDefault();
self.togglePlayPause();
}
});
// Enable audio on first user interaction
const enableAudioOnInteraction = function() {
console.log('π΅ User interaction detected - enabling audio playback');
// Remove this listener after first interaction
document.removeEventListener('click', enableAudioOnInteraction);
document.removeEventListener('keydown', enableAudioOnInteraction);
document.removeEventListener('touchstart', enableAudioOnInteraction);
// Try to play a silent audio to unlock audio context
if (self.audio && self.audio.src) {
self.audio.play().then(() => {
console.log('π΅ Audio unlocked successfully');
self.audio.pause(); // Pause immediately
}).catch(error => {
console.log('π΅ Audio unlock failed:', error);
});
}
};
// Add listeners for user interaction
document.addEventListener('click', enableAudioOnInteraction);
document.addEventListener('keydown', enableAudioOnInteraction);
document.addEventListener('touchstart', enableAudioOnInteraction);
},
async loadCommunityPlaylist() {
try {
console.log('π΅ Loading community playlist from database...');
// Load tracks from database via PHP
const response = await fetch('/api/get_community_tracks.php');
if (response.ok) {
const data = await response.json();
console.log('π΅ Community tracks loaded:', data);
if (data.success && data.tracks && data.tracks.length > 0) {
this.playlist = data.tracks;
console.log('π΅ Loaded', this.playlist.length, 'community tracks');
// Update playlist header
const playlistHeader = document.querySelector('#globalPlaylistPanel h3');
if (playlistHeader) {
playlistHeader.textContent = `Community Tracks (${this.playlist.length})`;
}
// Update track count display
const trackCountSpan = document.getElementById('globalTrackCount');
const artistNameSpan = document.getElementById('globalTrackArtistName');
if (trackCountSpan && artistNameSpan) {
artistNameSpan.textContent = `${this.playlist.length} community tracks loaded`;
trackCountSpan.textContent = `Ready to play`;
trackCountSpan.style.display = 'inline';
}
} else {
console.log('π΅ No community tracks found, loading fallback tracks');
this.loadFallbackTracks();
}
} else {
console.error('π΅ Failed to load community tracks:', response.status);
this.loadFallbackTracks();
}
this.renderPlaylist();
} catch (error) {
console.error('π΅ Error loading community playlist:', error);
this.loadFallbackTracks();
this.renderPlaylist();
}
},
loadFallbackTracks() {
console.log('π΅ Loading fallback tracks...');
// Fallback tracks from CDN
this.playlist = [
{
id: 'fallback1',
title: 'Lounge Lyrics',
artist_name: 'stephane bergeron',
audio_url: 'https://apiboxfiles.erweima.ai/MTk4YTg3OGYtM2Y4NS00YWJhLWIxMjMtMjk1OWFjOTUwMDFk.mp3',
user_id: null
},
{
id: 'fallback2',
title: 'Chains of Dominion',
artist_name: 'stephane bergeron',
audio_url: 'https://apiboxfiles.erweima.ai/MTk4YTg3OGYtM2Y4NS00YWJhLWIxMjMtMjk1OWFjOTUwMDFk.mp3',
user_id: null
},
{
id: 'fallback3',
title: 'Deep Groove',
artist_name: 'stephane bergeron',
audio_url: 'https://apiboxfiles.erweima.ai/MTk4YTg3OGYtM2Y4NS00YWJhLWIxMjMtMjk1OWFjOTUwMDFk.mp3',
user_id: null
}
];
console.log('π΅ Loaded', this.playlist.length, 'fallback tracks');
// Update playlist header
const playlistHeader = document.querySelector('#globalPlaylistPanel h3');
if (playlistHeader) {
playlistHeader.textContent = `Demo Tracks (${this.playlist.length})`;
}
// Update track count display
const trackCountSpan = document.getElementById('globalTrackCount');
const artistNameSpan = document.getElementById('globalTrackArtistName');
if (trackCountSpan && artistNameSpan) {
artistNameSpan.textContent = `${this.playlist.length} demo tracks loaded`;
trackCountSpan.textContent = `Ready to play`;
trackCountSpan.style.display = 'inline';
}
},
async loadCommunityPlaylistAndPlay() {
console.log('π΅ Loading community playlist and preparing to play...');
try {
await this.loadCommunityPlaylist();
if (this.playlist.length > 0) {
console.log('π΅ Community playlist loaded, ready to play tracks');
this.currentTrackIndex = 0;
return Promise.resolve();
} else {
console.log('π΅ No community tracks available');
this.currentTrackIndex = -1;
return Promise.resolve();
}
} catch (error) {
console.error('π΅ Error loading community playlist and play:', error);
this.currentTrackIndex = -1;
return Promise.resolve();
}
},
setupAutoPlay() {
// Auto-play disabled - user must click play button
console.log('π΅ Auto-play disabled - user must click play button');
},
renderPlaylist() {
console.log('π΅ Rendering community playlist with', this.playlist.length, 'tracks');
const container = document.getElementById('globalPlaylistTracks');
if (!container) {
console.error('π΅ Playlist container not found!');
return;
}
// Hide loading indicator
const loadingIndicator = document.getElementById('playlistLoading');
if (loadingIndicator) {
loadingIndicator.style.display = 'none';
}
container.innerHTML = '';
if (this.playlist.length === 0) {
container.innerHTML = `
<div class="playlist-loading">
<i class="fas fa-exclamation-triangle"></i>
<span>No tracks available</span>
<button onclick="window.globalPlayer.loadCommunityPlaylist()" style="margin-top: 1rem; padding: 0.5rem 1rem; background: #667eea; color: white; border: none; border-radius: 6px; cursor: pointer;">
<i class="fas fa-refresh"></i> Retry
</button>
</div>
`;
return;
}
this.playlist.forEach((track, index) => {
console.log('π΅ Rendering community track:', track.title);
const trackElement = document.createElement('div');
trackElement.className = 'playlist-track';
if (index === this.currentTrackIndex) {
trackElement.classList.add('active');
}
trackElement.innerHTML = `
<div class="playlist-track-info">
<div class="playlist-track-title">${track.title}</div>
<div class="playlist-track-artist">${track.artist_name || 'Unknown Artist'}</div>
</div>
<div class="playlist-track-actions">
<button class="playlist-play-btn" title="Play this track">
<i class="fas fa-play"></i>
</button>
</div>
`;
const self = this;
trackElement.addEventListener('click', function() {
console.log('π΅ Community track clicked:', track.title);
console.log('π΅ Audio URL from CDN:', track.audio_url);
self.wasPlaying = self.isPlaying;
self.playTrack(track.audio_url, track.title, track.artist_name || 'Unknown Artist', track.user_id);
// Update active state
document.querySelectorAll('.playlist-track').forEach(t => t.classList.remove('active'));
trackElement.classList.add('active');
self.currentTrackIndex = index;
});
// Add play button functionality
const playBtn = trackElement.querySelector('.playlist-play-btn');
if (playBtn) {
playBtn.addEventListener('click', function(e) {
e.stopPropagation();
console.log('π΅ Play button clicked for track:', track.title);
self.wasPlaying = self.isPlaying;
self.playTrack(track.audio_url, track.title, track.artist_name || 'Unknown Artist', track.user_id);
// Update active state
document.querySelectorAll('.playlist-track').forEach(t => t.classList.remove('active'));
trackElement.classList.add('active');
self.currentTrackIndex = index;
});
}
container.appendChild(trackElement);
});
console.log('π΅ Community playlist rendered successfully');
},
playTrack(audioUrl, title, artist, userId = null) {
console.log('π΅ Playing track:', { audioUrl, title, artist });
// Pause current audio if playing
if (this.audio) {
this.audio.pause();
this.isPlaying = false;
}
// Create new audio element
this.audio = new Audio(audioUrl);
this.audio.volume = this.volume;
console.log('π΅ Created new audio element with volume:', this.volume);
// FORCE SHOW THE PLAYER - This is the key fix!
this.showPlayer();
console.log('π΅ Forced show player in playTrack method');
// Update track info immediately
this.updateTrackInfo(title, artist, userId);
// Ensure volume slider reflects current volume
const volumeSlider = document.getElementById('globalVolumeSlider');
if (volumeSlider) {
volumeSlider.value = this.volume * 100;
}
// Firefox audio context unlock
const self = this;
this.audio.addEventListener('canplaythrough', function() {
console.log('π΅ Audio ready to play in Firefox:', title);
// Try to unlock audio context for Firefox
if (self.wasPlaying) {
console.log('π΅ Attempting to play in Firefox...');
self.audio.play().then(() => {
console.log('π΅ Firefox audio playing successfully!');
self.isPlaying = true;
self.updatePlayButton();
}).catch(error => {
console.error('π΅ Firefox audio play failed:', error);
// Try to unlock by playing and pausing
self.audio.play().then(() => {
console.log('π΅ Firefox audio unlocked, pausing...');
self.audio.pause();
self.isPlaying = false;
self.updatePlayButton();
alert('Firefox audio context unlocked! Click play again.');
}).catch(unlockError => {
console.error('π΅ Firefox audio unlock failed:', unlockError);
self.isPlaying = false;
self.updatePlayButton();
});
});
} else {
console.log('π΅ Audio ready, waiting for user to click play in Firefox');
self.isPlaying = false;
self.updatePlayButton();
}
});
// Add error handling for audio loading - FIXED WITH BINDING
const self = this;
this.audio.addEventListener('error', function(e) {
console.error('π΅ Audio loading error:', e);
console.error('π΅ Failed URL:', audioUrl);
self.updateTrackInfo('Audio Error', 'Failed to load track');
// Try to play next track if available
if (self.playlist.length > 0) {
console.log('π΅ Trying next track due to error');
setTimeout(() => {
self.nextTrack();
}, 2000);
}
});
this.audio.addEventListener('loadedmetadata', function() {
const totalTimeElement = document.getElementById('globalTotalTime');
if (totalTimeElement && self.audio.duration) {
totalTimeElement.textContent = self.formatTime(self.audio.duration);
}
});
this.audio.addEventListener('loadeddata', function() {
console.log('π΅ Audio loaded data event fired');
// Track is loaded and ready, but don't auto-play
self.isPlaying = false;
self.updatePlayButton();
console.log('π΅ Track loaded, ready for user interaction');
});
this.audio.addEventListener('timeupdate', function() {
if (self.audio.duration) {
const progress = (self.audio.currentTime / self.audio.duration) * 100;
const progressFill = document.getElementById('globalProgressFill');
const currentTimeElement = document.getElementById('globalCurrentTime');
if (progressFill) {
progressFill.style.width = progress + '%';
}
if (currentTimeElement) {
currentTimeElement.textContent = self.formatTime(self.audio.currentTime);
}
}
});
this.audio.addEventListener('ended', function() {
console.log('π΅ Track ended, auto-playing next track...');
// Add a small delay to ensure the audio element is fully stopped
setTimeout(() => {
self.nextTrack();
}, 100);
});
// Wait for audio to be ready before allowing play - FIXED WITH BINDING
this.audio.addEventListener('canplaythrough', function() {
console.log('π΅ Audio ready to play:', title);
console.log('π΅ wasPlaying state:', self.wasPlaying);
// Only auto-play if user explicitly initiated the play
if (self.wasPlaying) {
console.log('π΅ Auto-playing new track (user initiated)');
self.playAudio();
} else {
// Track is ready but waiting for user to click play
console.log('π΅ Track ready, waiting for user to click play button');
self.isPlaying = false;
self.updatePlayButton();
}
});
} else {
console.log('π΅ Audio loaded and ready - waiting for user to click play');
// Update UI to show it's ready
self.updatePlayButton();
}
});
// Fallback: If canplaythrough doesn't fire quickly enough, just update UI - FIXED WITH BINDING
setTimeout(() => {
if (!self.isPlaying && self.audio && self.audio.readyState >= 2) {
console.log('π΅ Fallback: Audio ready, updating UI');
self.updatePlayButton();
}
}, 1000);
// Update track info immediately
this.updateTrackInfo(title, artist, userId);
this.updatePlayButton();
this.showPlayer();
// Debug: Check if track info was actually updated
setTimeout(() => {
const trackTitle = document.getElementById('globalTrackTitle');
const trackArtist = document.getElementById('globalTrackArtistName');
console.log('π΅ Debug - Track title element:', trackTitle);
console.log('π΅ Debug - Track title content:', trackTitle ? trackTitle.textContent : 'null');
console.log('π΅ Debug - Track artist element:', trackArtist);
console.log('π΅ Debug - Track artist content:', trackArtist ? trackArtist.textContent : 'null');
console.log('π΅ Debug - Expected title:', title);
console.log('π΅ Debug - Expected artist:', artist);
}, 100);
// No playlist needed for individual track playback
this.currentTrackIndex = -1;
console.log('π΅ Individual track playback - no playlist needed');
},
togglePlayPause() {
console.log('π΅ togglePlayPause called - audio exists:', !!this.audio, 'playlist length:', this.playlist.length);
console.log('π΅ Current track index:', this.currentTrackIndex);
console.log('π΅ Is playing:', this.isPlaying);
console.log('π΅ Audio element:', this.audio);
console.log('π΅ Audio src:', this.audio ? this.audio.src : 'no audio');
console.log('π΅ Audio paused:', this.audio ? this.audio.paused : 'no audio');
// If no audio element exists, load default track
if (!this.audio) {
console.log('π΅ No audio loaded, loading default track...');
this.loadDefaultTrack();
return;
}
}
// Check if audio is ready to play
if (this.audio.readyState < 2) {
console.log('π΅ Audio not ready, waiting for data...');
// Wait for audio to be ready
this.audio.addEventListener('canplay', () => {
console.log('π΅ Audio ready, now playing...');
this.playAudio();
}, { once: true });
return;
}
if (this.isPlaying) {
console.log('π΅ Pausing audio');
this.pauseAudio();
} else {
console.log('π΅ Playing audio');
this.playAudio();
}
},
playAudio() {
if (!this.audio) {
console.error('π΅ No audio element to play');
return;
}
// Force pause all audio elements on the page first
const allAudio = document.querySelectorAll('audio');
console.log('π΅ Found', allAudio.length, 'audio elements on page');
allAudio.forEach((audio, index) => {
console.log('π΅ Audio', index, 'paused:', audio.paused, 'src:', audio.src);
if (!audio.paused && audio !== this.audio) {
audio.pause();
console.log('π΅ Paused other audio element:', audio.src);
}
});
// Try to play the audio
this.audio.play().then(() => {
this.isPlaying = true;
this.wasPlaying = true;
this.updatePlayButton();
console.log('π΅ Play successful');
}).catch(error => {
console.error('π΅ Play failed:', error);
this.isPlaying = false;
this.wasPlaying = false;
this.updatePlayButton();
// If play failed due to user interaction policy, handle gracefully
if (error.name === 'NotAllowedError') {
console.log('π΅ Play blocked by browser - user must interact first');
// Update track info and show play button - don't auto-play
if (this.playlist.length > 0 && this.currentTrackIndex === -1) {
const track = this.playlist[0];
this.currentTrackIndex = 0;
this.updateTrackInfo(track.title, track.artist_name || 'Unknown Artist', track.user_id);
}
// Don't show any alerts - just let user click play when ready
}
});
},
pauseAudio() {
if (this.audio) {
this.audio.pause();
console.log('π΅ Paused global player audio');
}
this.isPlaying = false;
this.wasPlaying = false;
this.updatePlayButton();
},
nextTrack() {
console.log('π΅ nextTrack called - playlist length:', this.playlist.length, 'current index:', this.currentTrackIndex);
if (this.playlist.length === 0) {
console.log('π΅ No tracks in playlist, cannot play next');
return;
}
// Store current playing state
this.wasPlaying = this.isPlaying;
// If currentTrackIndex is -1 or invalid, start from the beginning
if (this.currentTrackIndex < 0 || this.currentTrackIndex >= this.playlist.length) {
this.currentTrackIndex = 0;
} else {
this.currentTrackIndex = (this.currentTrackIndex + 1) % this.playlist.length;
}
const track = this.playlist[this.currentTrackIndex];
console.log('π΅ Playing next track:', track.title, 'at index:', this.currentTrackIndex);
this.playTrack(track.audio_url, track.title, track.artist_name || 'Unknown Artist', track.user_id);
},
previousTrack() {
if (this.playlist.length === 0) return;
// Store current playing state
this.wasPlaying = this.isPlaying;
this.currentTrackIndex = this.currentTrackIndex === 0 ? this.playlist.length - 1 : this.currentTrackIndex - 1;
const track = this.playlist[this.currentTrackIndex];
this.playTrack(track.audio_url, track.title, track.artist_name || 'Unknown Artist', track.user_id);
},
stopTrack() {
console.log('π΅ Stop track called');
if (this.audio) {
this.audio.pause();
this.audio.currentTime = 0;
console.log('π΅ Audio stopped and reset to beginning');
}
this.isPlaying = false;
this.wasPlaying = false;
this.updatePlayButton();
// Reset progress bar
const progressFill = document.getElementById('globalProgressFill');
if (progressFill) {
progressFill.style.width = '0%';
}
// Reset time display
const currentTime = document.getElementById('globalCurrentTime');
if (currentTime) {
currentTime.textContent = '0:00';
}
console.log('π΅ Track stopped successfully');
},
updateTrackInfo(title, artist, userId = null) {
console.log('π΅ updateTrackInfo called with:', { title, artist, userId });
const trackTitleElement = document.getElementById('globalTrackTitle');
const trackArtistElement = document.getElementById('globalTrackArtistName');
if (trackTitleElement) {
trackTitleElement.textContent = title;
console.log('π΅ Updated track title to:', title);
} else {
console.error('π΅ globalTrackTitle element not found!');
}
if (trackArtistElement) {
trackArtistElement.textContent = artist;
console.log('π΅ Updated track artist to:', artist);
} else {
console.error('π΅ globalTrackArtistName element not found!');
}
// Show/hide artist profile link
const artistLink = document.getElementById('globalTrackArtistLink');
if (userId && userId !== 'Unknown Artist') {
artistLink.href = `/artist_profile.php?id=${userId}`;
artistLink.style.display = 'inline-flex';
} else {
artistLink.style.display = 'none';
}
console.log('π΅ updateTrackInfo completed');
},
updatePlayButton() {
// Find the current play button and its icon
const playButton = document.getElementById('globalPlayBtn');
if (playButton) {
const icon = playButton.querySelector('i');
if (icon) {
icon.className = this.isPlaying ? 'fas fa-pause' : 'fas fa-play';
console.log('π΅ Updated play button icon to:', icon.className);
} else {
console.error('π΅ Icon element not found in play button');
}
} else {
console.error('π΅ Play button not found for icon update');
}
},
updateVolumeIcon() {
// Find the current volume icon
const volumeIcon = document.getElementById('globalVolumeIcon');
if (volumeIcon) {
if (this.volume === 0) {
volumeIcon.className = 'fas fa-volume-mute';
} else if (this.volume < 0.3) {
volumeIcon.className = 'fas fa-volume-down';
} else if (this.volume < 0.7) {
volumeIcon.className = 'fas fa-volume-off';
} else {
volumeIcon.className = 'fas fa-volume-up';
}
console.log('π΅ Updated volume icon to:', volumeIcon.className);
} else {
console.error('π΅ Volume icon element not found');
}
},
showPlayer() {
console.log('π΅ Showing global player');
const playerElement = document.getElementById('globalMusicPlayer');
if (playerElement) {
playerElement.classList.add('active');
// Force visibility with inline styles - ALWAYS VISIBLE
playerElement.style.display = 'flex';
playerElement.style.opacity = '1';
playerElement.style.visibility = 'visible';
playerElement.style.transform = 'translateY(0)';
playerElement.style.zIndex = '9999';
playerElement.style.position = 'fixed';
playerElement.style.bottom = '0';
playerElement.style.left = '0';
playerElement.style.right = '0';
console.log('π΅ Global player forced visible and positioned at bottom');
} else {
console.error('π΅ Global player element not found!');
}
},
hidePlayer() {
document.getElementById('globalMusicPlayer').classList.remove('active');
},
togglePlaylist() {
console.log('π΅ Toggling playlist panel');
const panel = document.getElementById('globalPlaylistPanel');
if (panel) {
panel.classList.toggle('active');
console.log('π΅ Playlist panel active:', panel.classList.contains('active'));
// If opening playlist and no tracks loaded, try to load them
if (panel.classList.contains('active') && this.playlist.length === 0) {
console.log('π΅ Playlist empty, loading tracks...');
this.loadCommunityPlaylistAndPlay();
}
} else {
console.error('π΅ Playlist panel not found!');
}
},
formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
},
resumePlayback() {
console.log('π΅ Resuming playback after navigation');
// Ensure the player is visible and functional after navigation
this.showPlayer();
// If we have a current track and it was playing, try to resume
if (this.audio && this.isPlaying) {
console.log('π΅ Resuming current track');
this.audio.play().catch(error => {
console.log('π΅ Could not resume playback:', error);
// If resume fails, try to restart the current track
if (this.playlist.length > 0 && this.currentTrackIndex >= 0) {
const track = this.playlist[this.currentTrackIndex];
this.playTrack(track.audio_url, track.title, track.artist_name || 'Unknown Artist', track.user_id);
}
});
} else if (this.playlist.length > 0 && this.currentTrackIndex >= 0) {
// If no audio but we have a playlist, restart the current track
console.log('π΅ Restarting current track from playlist');
const track = this.playlist[this.currentTrackIndex];
this.playTrack(track.audio_url, track.title, track.artist_name || 'Unknown Artist', track.user_id);
} else if (this.playlist.length > 0) {
// If no current track but we have a playlist, start from the beginning
console.log('π΅ Starting playback from beginning of playlist');
this.currentTrackIndex = 0;
const track = this.playlist[0];
this.playTrack(track.audio_url, track.title, track.artist_name || 'Unknown Artist', track.user_id);
}
},
loadDefaultTrack() {
console.log('π΅ Loading default CDN track...');
// Use the first CDN track
const cdnTracks = [
{
audio_url: 'https://apiboxfiles.erweima.ai/MTk4YTg3OGYtM2Y4NS00YWJhLWIxMjMtMjk1OWFjOTUwMDFk.mp3',
title: 'Lounge Lyrics',
artist_name: 'stephane bergeron'
}
];
this.playTrack(cdnTracks[0].audio_url, cdnTracks[0].title, cdnTracks[0].artist_name, null);
this.wasPlaying = false; // Don't auto-play default track
}
};
// Initialize global player - FIXED VERSION
function initializeGlobalPlayer() {
if (typeof window.globalPlayer === 'undefined') {
window.globalPlayer = globalPlayer;
console.log('π΅ Global player object created');
// ALWAYS show the player when initializing
setTimeout(() => {
if (window.globalPlayer && typeof window.globalPlayer.showPlayer === 'function') {
window.globalPlayer.showPlayer();
console.log('π΅ Global player automatically shown on initialization');
}
}, 100);
// Wait for elements to exist before initializing
waitForElements();
} else {
console.log('π΅ Global player already exists, skipping initialization');
// Still ensure the player is visible and functional
if (window.globalPlayer.showPlayer) {
window.globalPlayer.showPlayer();
}
}
}
function waitForElements() {
const playBtn = document.getElementById('globalPlayBtn');
const playerElement = document.getElementById('globalMusicPlayer');
if (!playBtn || !playerElement) {
console.log('π΅ Elements not ready yet, retrying in 100ms...');
setTimeout(waitForElements, 100);
return;
}
console.log('π΅ All elements found, initializing global player...');
console.log('π΅ Play button found:', !!playBtn);
console.log('π΅ Player element found:', !!playerElement);
try {
window.globalPlayer.init().then(() => {
console.log('π΅ Global player initialized successfully');
}).catch(error => {
console.error('π΅ Error initializing global player:', error);
});
} catch (error) {
console.error('π΅ Error initializing global player:', error);
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
console.log('π΅ DOM loaded, initializing global player...');
// Ensure global player is assigned
if (typeof window.globalPlayer === 'undefined') {
window.globalPlayer = globalPlayer;
console.log('π΅ Global player assigned on DOM load');
}
initializeGlobalPlayer();
// Force show player immediately
setTimeout(() => {
const playerElement = document.getElementById('globalMusicPlayer');
if (playerElement) {
playerElement.style.display = 'flex';
playerElement.style.opacity = '1';
playerElement.style.visibility = 'visible';
console.log('π΅ Global player forced visible on DOM load');
}
}, 100);
});
} else {
console.log('π΅ DOM already loaded, initializing immediately');
// Ensure global player is assigned
if (typeof window.globalPlayer === 'undefined') {
window.globalPlayer = globalPlayer;
console.log('π΅ Global player assigned immediately');
}
initializeGlobalPlayer();
// Force show player immediately
setTimeout(() => {
const playerElement = document.getElementById('globalMusicPlayer');
if (playerElement) {
playerElement.style.display = 'flex';
playerElement.style.opacity = '1';
playerElement.style.visibility = 'visible';
console.log('π΅ Global player forced visible immediately');
}
}, 100);
}
// Fallback initialization - ensure global player is available
setTimeout(() => {
if (typeof window.globalPlayer === 'undefined') {
console.log('π΅ Fallback: Global player not found, attempting initialization...');
initializeGlobalPlayer();
}
}, 2000);
// Additional fallback for slow loading pages
setTimeout(() => {
if (typeof window.globalPlayer === 'undefined') {
console.log('π΅ Final fallback: Global player still not found, forcing initialization...');
if (typeof initializeGlobalPlayer === 'function') {
initializeGlobalPlayer();
}
}
}, 5000);
// Make global player available globally immediately
window.globalPlayer = globalPlayer;
console.log('π΅ Global player object created and assigned to window.globalPlayer immediately');
// Also assign it as a fallback in case the above doesn't work
if (typeof window.globalPlayer === 'undefined') {
window.globalPlayer = globalPlayer;
console.log('π΅ Fallback: Global player assigned to window.globalPlayer');
}
// Global function to ensure global player is available
window.ensureGlobalPlayer = async function() {
if (typeof window.globalPlayer === 'undefined') {
console.error('π΅ Global player not found!');
return false;
}
if (!window.globalPlayer.initialized) {
console.log('π΅ Global player not initialized, initializing now...');
try {
await window.globalPlayer.init();
return true;
} catch (error) {
console.error('π΅ Failed to initialize global player:', error);
// Mark as initialized anyway to prevent infinite retries
window.globalPlayer.initialized = true;
return true;
}
}
return true;
};
// Global function to play track with global player
window.playTrackWithGlobalPlayer = function(audioUrl, title, artist, userId = null) {
console.log('π΅ playTrackWithGlobalPlayer called:', { audioUrl, title, artist });
// Ensure global player is available
if (typeof window.globalPlayer === 'undefined') {
console.log('π΅ Global player not available, creating it...');
window.globalPlayer = globalPlayer;
}
// Use the global player directly
if (window.globalPlayer && window.globalPlayer.playTrack) {
console.log('π΅ Using global player to play track');
try {
// Set wasPlaying to true so the track auto-plays
window.globalPlayer.wasPlaying = true;
window.globalPlayer.playTrack(audioUrl, title, artist, userId);
return true;
} catch (error) {
console.error('π΅ Error calling global player playTrack:', error);
return false;
}
} else {
console.error('π΅ Global player playTrack function not available');
console.log('π΅ window.globalPlayer:', typeof window.globalPlayer);
if (typeof window.globalPlayer !== 'undefined') {
console.log('π΅ window.globalPlayer.playTrack:', typeof window.globalPlayer.playTrack);
}
return false;
}
};
// Simple test function for debugging
window.testGlobalPlayer = function() {
console.log('π΅ Testing global player...');
if (window.globalPlayer && window.globalPlayer.playTrack) {
console.log('π΅ Global player is working');
console.log('π΅ Global player working - using CDN tracks only');
} else {
console.error('π΅ Global player not working');
}
};
// Comprehensive debug function
window.debugGlobalPlayer = function() {
console.log('π΅ === GLOBAL PLAYER DEBUG ===');
// Check if global player exists
console.log('π΅ Global player exists:', typeof window.globalPlayer !== 'undefined');
if (typeof window.globalPlayer !== 'undefined') {
console.log('π΅ Global player initialized:', window.globalPlayer.initialized);
console.log('π΅ Global player audio element:', !!window.globalPlayer.audio);
console.log('π΅ Global player is playing:', window.globalPlayer.isPlaying);
console.log('π΅ Global player volume:', window.globalPlayer.volume);
console.log('π΅ Global player playlist length:', window.globalPlayer.playlist.length);
console.log('π΅ Global player current track index:', window.globalPlayer.currentTrackIndex);
// Check DOM elements
const elements = [
'globalMusicPlayer',
'globalPlayBtn',
'globalStopBtn',
'globalPrevBtn',
'globalNextBtn',
'globalVolumeSlider',
'globalVolumeIcon',
'globalPlaylistToggle',
'globalPlaylistPanel',
'globalPlaylistTracks'
];
console.log('π΅ DOM Elements check:');
elements.forEach(id => {
const element = document.getElementById(id);
console.log(`π΅ ${id}:`, !!element);
});
// Check volume slider
const volumeSlider = document.getElementById('globalVolumeSlider');
if (volumeSlider) {
console.log('π΅ Volume slider value:', volumeSlider.value);
console.log('π΅ Volume slider event listeners:', volumeSlider.oninput !== null);
}
// Check playlist
const playlistContainer = document.getElementById('globalPlaylistTracks');
if (playlistContainer) {
console.log('π΅ Playlist container children:', playlistContainer.children.length);
}
// Test volume control
console.log('π΅ Testing volume control...');
if (window.globalPlayer.audio) {
console.log('π΅ Audio volume before test:', window.globalPlayer.audio.volume);
window.globalPlayer.volume = 0.5;
console.log('π΅ Audio volume after test:', window.globalPlayer.audio.volume);
}
// Test playlist toggle
console.log('π΅ Testing playlist toggle...');
const playlistPanel = document.getElementById('globalPlaylistPanel');
if (playlistPanel) {
console.log('π΅ Playlist panel active:', playlistPanel.classList.contains('active'));
}
}
console.log('π΅ === END DEBUG ===');
};
// Test button functionality
window.testButtons = function() {
console.log('π΅ Testing button functionality...');
const playBtn = document.getElementById('globalPlayBtn');
const stopBtn = document.getElementById('globalStopBtn');
console.log('π΅ Play button found:', !!playBtn);
console.log('π΅ Stop button found:', !!stopBtn);
if (playBtn) {
console.log('π΅ Play button onclick:', typeof playBtn.onclick);
console.log('π΅ Play button event listeners:', playBtn.onclick !== null);
}
if (stopBtn) {
console.log('π΅ Stop button onclick:', typeof stopBtn.onclick);
console.log('π΅ Stop button event listeners:', stopBtn.onclick !== null);
}
// Test clicking the buttons
if (playBtn) {
console.log('π΅ Simulating play button click...');
playBtn.click();
}
if (stopBtn) {
console.log('π΅ Simulating stop button click...');
stopBtn.click();
}
};
// DJ Interface Functionality
const djInterface = {
isActive: false,
deckA: {
track: null,
isPlaying: false,
currentTime: 0,
duration: 0,
bpm: 128,
cuePoints: [],
loop: null
},
deckB: {
track: null,
isPlaying: false,
currentTime: 0,
duration: 0,
bpm: 128,
cuePoints: [],
loop: null
},
mixer: {
crossfader: 0.5,
effects: {
filter: false,
echo: false,
reverb: false,
flanger: false
}
},
syncTimer: null,
init() {
console.log('ποΈ Initializing DJ Interface...');
this.setupEventListeners();
this.setupFaderControls();
this.setupCrossfader();
this.setupEQKnobs();
console.log('ποΈ DJ Interface initialized');
},
setupEventListeners() {
// DJ Interface toggle
const djToggle = document.getElementById('globalDJToggle');
const djPanel = document.getElementById('globalDJPanel');
const closeDJ = document.getElementById('globalCloseDJ');
if (djToggle) {
djToggle.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.toggleDJInterface();
});
}
if (closeDJ) {
closeDJ.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.toggleDJInterface();
});
}
},
setupFaderControls() {
const faders = ['A', 'B'];
faders.forEach(deck => {
const fader = document.getElementById(`fader${deck}`);
const handle = document.getElementById(`faderHandle${deck}`);
if (fader && handle) {
fader.addEventListener('mousedown', (e) => {
const rect = fader.getBoundingClientRect();
const y = e.clientY - rect.top;
const percentage = Math.max(0, Math.min(1, 1 - (y / rect.height)));
handle.style.bottom = `${percentage * 100}%`;
this.updateMixerLevels();
});
}
});
},
setupCrossfader() {
const crossfader = document.getElementById('crossfader');
const handle = document.getElementById('crossfaderHandle');
if (crossfader && handle) {
crossfader.addEventListener('mousedown', (e) => {
const rect = crossfader.getBoundingClientRect();
const x = e.clientX - rect.left;
const percentage = Math.max(0, Math.min(1, x / rect.width));
handle.style.left = `${percentage * 100}%`;
this.mixer.crossfader = percentage;
this.updateMixerLevels();
});
}
},
setupEQKnobs() {
const decks = ['A', 'B'];
const bands = ['High', 'Mid', 'Low'];
decks.forEach(deck => {
bands.forEach(band => {
const knob = document.getElementById(`eq${band}${deck}`);
if (knob) {
knob.addEventListener('click', () => {
console.log(`ποΈ ${band} EQ clicked for Deck ${deck}`);
});
}
});
});
},
toggleDJInterface() {
const djPanel = document.getElementById('globalDJPanel');
if (!djPanel) return;
this.isActive = !this.isActive;
if (this.isActive) {
djPanel.classList.add('active');
document.body.style.paddingBottom = '480px'; // Account for DJ interface height
console.log('ποΈ Pioneer DDJ-FLX10 Interface activating...');
// Reset loading state for Pioneer sequence
const loadingOverlay = document.getElementById('loadingOverlay');
const interactiveOverlay = document.getElementById('interactiveOverlay');
if (loadingOverlay && interactiveOverlay) {
loadingOverlay.style.display = 'flex';
loadingOverlay.style.opacity = '1';
interactiveOverlay.style.display = 'none';
interactiveOverlay.style.opacity = '0';
}
// Start Pioneer loading sequence
setTimeout(() => {
initPioneerController();
}, 500);
// Auto-load current track from global player to Deck A
this.autoLoadCurrentTrack();
// Start sync timer
this.startSyncTimer();
} else {
djPanel.classList.remove('active');
document.body.style.paddingBottom = '80px'; // Normal padding for global player
console.log('ποΈ Pioneer DDJ-FLX10 Interface deactivated');
// Stop sync timer
this.stopSyncTimer();
}
},
autoLoadCurrentTrack() {
// Check if global player has a track loaded
if (window.globalPlayer && window.globalPlayer.audio && window.globalPlayer.audio.src) {
const currentTrack = {
title: document.getElementById('globalTrackTitle')?.textContent || 'Current Track',
artist: document.getElementById('globalTrackArtistName')?.textContent || 'Unknown Artist',
audioUrl: window.globalPlayer.audio.src,
bpm: 128, // Default BPM - could be enhanced with BPM detection
duration: window.globalPlayer.audio.duration || 180,
currentTime: window.globalPlayer.audio.currentTime || 0
};
// Load to Deck A
this.loadTrackToDeck('A', currentTrack);
// If the track is currently playing, sync the playback
if (window.globalPlayer.isPlaying) {
// Set the deck to playing state
this.deckA.isPlaying = true;
this.deckA.currentTime = currentTrack.currentTime;
// Update UI to show playing state
const statusElement = document.getElementById('deckAStatus');
const playButton = document.getElementById('playA');
if (statusElement) {
statusElement.textContent = 'PLAYING';
statusElement.className = 'deck-status playing';
}
if (playButton) {
playButton.textContent = 'βΈοΈ';
}
// Start playback simulation from current position
this.simulatePlayback('A');
console.log('ποΈ Current track loaded and synced to Deck A');
} else {
console.log('ποΈ Current track loaded to Deck A (paused)');
}
} else {
console.log('ποΈ No track currently playing in global player');
}
},
loadTrackToDeck(deck, track) {
const deckState = this[`deck${deck}`];
deckState.track = track;
deckState.bpm = track.bpm || 128;
deckState.duration = track.duration || 180;
// If track has currentTime, use it (for syncing with global player)
if (track.currentTime !== undefined) {
deckState.currentTime = track.currentTime;
} else {
deckState.currentTime = 0;
}
// Update UI
const trackInfo = document.getElementById(`trackInfo${deck}`);
const bpmDisplay = document.getElementById(`bpm${deck}`);
const playhead = document.getElementById(`playhead${deck}`);
if (trackInfo) {
trackInfo.innerHTML = `
<div class="track-title">${track.title}</div>
<div class="track-artist">${track.artist}</div>
<div class="track-time">${this.formatTime(deckState.currentTime)} / ${this.formatTime(track.duration)}</div>
`;
}
if (bpmDisplay) {
bpmDisplay.textContent = `${track.bpm || 128} BPM`;
}
// Update playhead position if track has current time
if (playhead && deckState.currentTime > 0) {
const progress = deckState.currentTime / deckState.duration;
playhead.style.left = `${progress * 100}%`;
}
console.log(`ποΈ Track loaded to Deck ${deck}:`, track.title, `at ${this.formatTime(deckState.currentTime)}`);
},
playDeck(deck) {
const deckState = this[`deck${deck}`];
const statusElement = document.getElementById(`deck${deck}Status`);
const playButton = document.getElementById(`play${deck}`);
if (!deckState.track) {
console.log(`ποΈ No track loaded in Deck ${deck}`);
return;
}
if (deckState.isPlaying) {
// Pause
deckState.isPlaying = false;
statusElement.textContent = 'PAUSED';
statusElement.className = 'deck-status stopped';
playButton.textContent = 'βΆοΈ';
} else {
// Play
deckState.isPlaying = true;
statusElement.textContent = 'PLAYING';
statusElement.className = 'deck-status playing';
playButton.textContent = 'βΈοΈ';
// Start playback simulation
this.simulatePlayback(deck);
}
this.updateMasterLevel();
},
simulatePlayback(deck) {
const deckState = this[`deck${deck}`];
const playhead = document.getElementById(`playhead${deck}`);
const trackInfo = document.getElementById(`trackInfo${deck}`);
if (!deckState.isPlaying) return;
// Update current time
deckState.currentTime += 0.1;
if (deckState.currentTime >= deckState.duration) {
deckState.currentTime = 0;
deckState.isPlaying = false;
const statusElement = document.getElementById(`deck${deck}Status`);
const playButton = document.getElementById(`play${deck}`);
statusElement.textContent = 'STOPPED';
statusElement.className = 'deck-status stopped';
playButton.textContent = 'βΆοΈ';
return;
}
// Update playhead position
const progress = deckState.currentTime / deckState.duration;
if (playhead) {
playhead.style.left = `${progress * 100}%`;
}
// Update time display
if (trackInfo) {
const timeElement = trackInfo.querySelector('.track-time');
if (timeElement) {
timeElement.textContent = `${this.formatTime(deckState.currentTime)} / ${this.formatTime(deckState.duration)}`;
}
}
// Continue simulation
setTimeout(() => this.simulatePlayback(deck), 100);
},
setCue(deck) {
const deckState = this[`deck${deck}`];
const cuePoint = deckState.currentTime;
deckState.cuePoints.push(cuePoint);
console.log(`ποΈ Cue point set for Deck ${deck} at ${this.formatTime(cuePoint)}`);
},
syncTracks() {
console.log('ποΈ Syncing tracks...');
const deckA = this.deckA;
const deckB = this.deckB;
if (deckA.track && deckB.track) {
// Adjust BPM to match
const targetBPM = deckA.bpm;
deckB.bpm = targetBPM;
document.getElementById('bpmB').textContent = `${targetBPM} BPM`;
console.log(`ποΈ Synced Deck B to ${targetBPM} BPM`);
}
},
toggleEffect(effect) {
this.mixer.effects[effect] = !this.mixer.effects[effect];
const button = event.target;
if (this.mixer.effects[effect]) {
button.classList.add('active');
} else {
button.classList.remove('active');
}
console.log(`ποΈ ${effect} effect ${this.mixer.effects[effect] ? 'enabled' : 'disabled'}`);
},
updateMixerLevels() {
console.log('ποΈ Mixer levels updated');
},
updateMasterLevel() {
const masterLevel = document.getElementById('masterLevel');
const deckA = this.deckA;
const deckB = this.deckB;
// Calculate master level based on playing tracks
let level = 0;
if (deckA.isPlaying) level += 0.3;
if (deckB.isPlaying) level += 0.3;
// Add some variation
level += Math.random() * 0.2;
level = Math.min(1, level);
if (masterLevel) {
masterLevel.style.width = `${level * 100}%`;
}
},
formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
},
// Load current track to deck
loadCurrentTrackToDeck(deck) {
if (window.globalPlayer && window.globalPlayer.audio) {
const currentTrack = {
title: document.getElementById('globalTrackTitle').textContent,
artist: document.getElementById('globalTrackArtistName').textContent,
audioUrl: window.globalPlayer.audio.src,
bpm: 128, // Default BPM
duration: window.globalPlayer.audio.duration || 180,
currentTime: window.globalPlayer.audio.currentTime || 0
};
this.loadTrackToDeck(deck, currentTrack);
// If the track is playing, sync the deck state
if (window.globalPlayer.isPlaying) {
this[`deck${deck}`].isPlaying = true;
this[`deck${deck}`].currentTime = currentTrack.currentTime;
// Update UI
const statusElement = document.getElementById(`deck${deck}Status`);
const playButton = document.getElementById(`play${deck}`);
if (statusElement) {
statusElement.textContent = 'PLAYING';
statusElement.className = 'deck-status playing';
}
if (playButton) {
playButton.textContent = 'βΈοΈ';
}
// Start playback simulation
this.simulatePlayback(deck);
}
console.log(`ποΈ Current track loaded to Deck ${deck}`);
} else {
console.log('ποΈ No current track to load');
}
},
// Sync DJ interface with global player time updates
syncWithGlobalPlayer() {
if (window.globalPlayer && window.globalPlayer.audio && this.isActive) {
const currentTime = window.globalPlayer.audio.currentTime;
const duration = window.globalPlayer.audio.duration;
// Update Deck A if it has the same track
if (this.deckA.track && this.deckA.track.audioUrl === window.globalPlayer.audio.src) {
this.deckA.currentTime = currentTime;
this.deckA.duration = duration;
// Update playhead
const playhead = document.getElementById('playheadA');
if (playhead && duration > 0) {
const progress = currentTime / duration;
playhead.style.left = `${progress * 100}%`;
}
// Update time display
const trackInfo = document.getElementById('trackInfoA');
if (trackInfo) {
const timeElement = trackInfo.querySelector('.track-time');
if (timeElement) {
timeElement.textContent = `${this.formatTime(currentTime)} / ${this.formatTime(duration)}`;
}
}
}
}
},
startSyncTimer() {
// Clear any existing timer
this.stopSyncTimer();
// Start new sync timer
this.syncTimer = setInterval(() => {
this.syncWithGlobalPlayer();
}, 100); // Sync every 100ms for smooth updates
},
stopSyncTimer() {
if (this.syncTimer) {
clearInterval(this.syncTimer);
this.syncTimer = null;
}
}
};
// Global DJ Interface functions
window.djPlayDeck = function(deck) {
djInterface.playDeck(deck);
};
window.djSetCue = function(deck) {
djInterface.setCue(deck);
};
window.djSyncTracks = function() {
djInterface.syncTracks();
};
window.djToggleEffect = function(effect) {
djInterface.toggleEffect(effect);
};
function initInteractiveController() {
console.log('ποΈ Initializing Interactive Pioneer Controller...');
// Jog Wheel Interaction
const jogWheels = document.querySelectorAll('.jog-wheel-interactive');
jogWheels.forEach(jogWheel => {
let isDragging = false;
let startY = 0;
let startRotation = 0;
jogWheel.addEventListener('mousedown', (e) => {
isDragging = true;
startY = e.clientY;
startRotation = 0;
jogWheel.style.cursor = 'grabbing';
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaY = e.clientY - startY;
const rotation = deltaY * 0.5; // Sensitivity
// Update display with rotation
const deck = jogWheel.dataset.deck;
const display = document.getElementById(`display${deck === 'A' ? 'Left' : 'Right'}`);
if (display) {
const pitchDisplay = display.children[3];
const pitch = Math.round((rotation / 10) * 100) / 100;
pitchDisplay.textContent = `${pitch > 0 ? '+' : ''}${pitch}%`;
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
jogWheel.style.cursor = 'grab';
});
});
// Performance Pads
const pads = document.querySelectorAll('.pad-interactive');
pads.forEach(pad => {
pad.addEventListener('click', () => {
const deck = pad.dataset.deck;
const padNum = pad.dataset.pad;
console.log(`Pad ${padNum} pressed on Deck ${deck}`);
// Visual feedback
pad.style.background = 'rgba(231, 76, 60, 0.8)';
setTimeout(() => {
pad.style.background = 'rgba(231, 76, 60, 0.2)';
}, 100);
});
});
// Play Buttons
const playBtns = document.querySelectorAll('.play-btn');
playBtns.forEach(btn => {
btn.addEventListener('click', () => {
const deck = btn.dataset.deck;
const icon = btn.querySelector('i');
if (icon.classList.contains('fa-play')) {
icon.classList.remove('fa-play');
icon.classList.add('fa-pause');
btn.style.background = 'rgba(231, 76, 60, 0.8)';
btn.style.borderColor = '#e74c3c';
console.log(`Deck ${deck} playing`);
} else {
icon.classList.remove('fa-pause');
icon.classList.add('fa-play');
btn.style.background = 'rgba(39, 174, 96, 0.8)';
btn.style.borderColor = '#27ae60';
console.log(`Deck ${deck} paused`);
}
});
});
// Cue Buttons
const cueBtns = document.querySelectorAll('.cue-btn');
cueBtns.forEach(btn => {
btn.addEventListener('click', () => {
const deck = btn.dataset.deck;
console.log(`Cue set for Deck ${deck}`);
// Visual feedback
btn.style.background = 'rgba(243, 156, 18, 1)';
setTimeout(() => {
btn.style.background = 'rgba(243, 156, 18, 0.8)';
}, 200);
});
});
// Tempo Faders
const tempoFaders = document.querySelectorAll('.tempo-fader');
tempoFaders.forEach(fader => {
const handle = fader.querySelector('.tempo-handle');
let isDragging = false;
let startY = 0;
let startTop = 50;
handle.addEventListener('mousedown', (e) => {
isDragging = true;
startY = e.clientY;
startTop = parseInt(handle.style.top) || 50;
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaY = e.clientY - startY;
const faderHeight = fader.offsetHeight;
const handleHeight = handle.offsetHeight;
const maxTop = faderHeight - handleHeight;
let newTop = startTop + (deltaY / faderHeight) * 100;
newTop = Math.max(0, Math.min(100, newTop));
handle.style.top = `${newTop}%`;
// Update tempo display
const deck = fader.dataset.deck;
const display = document.getElementById(`display${deck === 'A' ? 'Left' : 'Right'}`);
if (display) {
const bpmDisplay = display.children[2];
const baseBPM = 128.32;
const tempoChange = (newTop - 50) * 0.1; // Β±5 BPM range
const newBPM = (baseBPM + tempoChange).toFixed(2);
bpmDisplay.textContent = `${newBPM} BPM`;
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
});
// Channel Faders
const channelFaders = document.querySelectorAll('.channel-fader');
channelFaders.forEach(fader => {
const handle = fader.querySelector('.fader-handle');
let isDragging = false;
let startY = 0;
let startBottom = 30;
handle.addEventListener('mousedown', (e) => {
isDragging = true;
startY = e.clientY;
startBottom = parseInt(handle.style.bottom) || 30;
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaY = e.clientY - startY;
const faderHeight = fader.offsetHeight;
let newBottom = startBottom - (deltaY / faderHeight) * 100;
newBottom = Math.max(0, Math.min(100, newBottom));
handle.style.bottom = `${newBottom}%`;
// Update level meter
const channel = fader.dataset.channel;
const meter = document.getElementById(`levelMeter${channel}`);
if (meter) {
const bar = meter.querySelector('.meter-bar');
bar.style.height = `${newBottom}%`;
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
});
// Crossfader
const crossfader = document.getElementById('crossfaderInteractive');
const crossfaderHandle = document.getElementById('crossfaderHandleInteractive');
let isCrossfaderDragging = false;
let startX = 0;
let startLeft = 50;
crossfaderHandle.addEventListener('mousedown', (e) => {
isCrossfaderDragging = true;
startX = e.clientX;
startLeft = parseInt(crossfaderHandle.style.left) || 50;
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isCrossfaderDragging) return;
const deltaX = e.clientX - startX;
const faderWidth = crossfader.offsetWidth;
let newLeft = startLeft + (deltaX / faderWidth) * 100;
newLeft = Math.max(0, Math.min(100, newLeft));
crossfaderHandle.style.left = `${newLeft}%`;
});
document.addEventListener('mouseup', () => {
isCrossfaderDragging = false;
});
// EQ Knobs
const eqKnobs = document.querySelectorAll('.eq-knob');
eqKnobs.forEach(knob => {
let isDragging = false;
let startY = 0;
let startRotation = 0;
knob.addEventListener('mousedown', (e) => {
isDragging = true;
startY = e.clientY;
startRotation = 0;
knob.style.cursor = 'grabbing';
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaY = e.clientY - startY;
const rotation = deltaY * 2; // Sensitivity
knob.style.transform = `rotate(${rotation}deg)`;
});
document.addEventListener('mouseup', () => {
isDragging = false;
knob.style.cursor = 'grab';
});
});
// Master Knob
const masterKnob = document.getElementById('masterKnob');
let isMasterDragging = false;
let startY = 0;
let startRotation = 0;
masterKnob.addEventListener('mousedown', (e) => {
isMasterDragging = true;
startY = e.clientY;
startRotation = 0;
masterKnob.style.cursor = 'grabbing';
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isMasterDragging) return;
const deltaY = e.clientY - startY;
const rotation = deltaY * 2;
masterKnob.style.transform = `translateX(-50%) rotate(${rotation}deg)`;
});
document.addEventListener('mouseup', () => {
isMasterDragging = false;
masterKnob.style.cursor = 'grab';
});
// Beat Sync Buttons
const beatSyncBtns = document.querySelectorAll('.beat-sync-btn');
beatSyncBtns.forEach(btn => {
btn.addEventListener('click', () => {
const deck = btn.dataset.deck;
console.log(`Beat Sync toggled for Deck ${deck}`);
btn.classList.toggle('active');
if (btn.classList.contains('active')) {
btn.style.background = 'rgba(39, 174, 96, 0.8)';
} else {
btn.style.background = 'rgba(39, 174, 96, 0.3)';
}
});
});
// Master Button
const masterBtn = document.getElementById('masterBtnInteractive');
masterBtn.addEventListener('click', () => {
console.log('Master button pressed');
masterBtn.classList.toggle('active');
if (masterBtn.classList.contains('active')) {
masterBtn.style.background = 'rgba(231, 76, 60, 0.8)';
} else {
masterBtn.style.background = 'rgba(231, 76, 60, 0.3)';
}
});
// Cue Channel Buttons
const cueChannelBtns = document.querySelectorAll('.cue-channel-btn');
cueChannelBtns.forEach(btn => {
btn.addEventListener('click', () => {
const channel = btn.dataset.channel;
console.log(`Cue toggled for Channel ${channel}`);
btn.classList.toggle('active');
if (btn.classList.contains('active')) {
btn.style.background = 'rgba(243, 156, 18, 0.8)';
btn.style.borderColor = '#e67e22';
} else {
btn.style.background = 'rgba(243, 156, 18, 0.3)';
btn.style.borderColor = '#f39c12';
}
});
});
// Crossfader Assignment
const assignBtns = document.querySelectorAll('.assign-btn-interactive');
assignBtns.forEach(btn => {
btn.addEventListener('click', () => {
const assign = btn.dataset.assign;
console.log(`Crossfader assignment: ${assign}`);
// Remove active from all buttons
assignBtns.forEach(b => b.classList.remove('active'));
// Add active to clicked button
btn.classList.add('active');
});
});
console.log('ποΈ Interactive Pioneer Controller initialized!');
}
});
// ========================================
// π PARTICLE METAVERSE DJ SYSTEM
// ========================================
class ParticleMetaverseDJ {
constructor() {
this.canvas = null;
this.ctx = null;
this.particles = [];
this.audioAnalyser = null;
this.frequencyData = null;
this.isActive = false;
this.particleSystems = {
jogWheelA: [],
jogWheelB: [],
waveformA: [],
waveformB: [],
mixer: []
};
}
init() {
console.log('π Initializing Particle Metaverse DJ System...');
this.setupCanvas();
this.setupAudioAnalysis();
this.createParticleSystems();
this.startParticleLoop();
}
setupCanvas() {
// Create hidden canvas for particle rendering
this.canvas = document.createElement('canvas');
this.canvas.style.position = 'absolute';
this.canvas.style.top = '0';
this.canvas.style.left = '0';
this.canvas.style.pointerEvents = 'none';
this.canvas.style.zIndex = '9997';
this.ctx = this.canvas.getContext('2d');
// Add to DJ interface
const djPanel = document.getElementById('globalDJPanel');
if (djPanel) {
djPanel.appendChild(this.canvas);
this.resizeCanvas();
}
}
setupAudioAnalysis() {
if (window.globalPlayer && window.globalPlayer.audio) {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const source = audioContext.createMediaElementSource(window.globalPlayer.audio);
this.audioAnalyser = audioContext.createAnalyser();
source.connect(this.audioAnalyser);
this.audioAnalyser.connect(audioContext.destination);
this.audioAnalyser.fftSize = 256;
this.frequencyData = new Uint8Array(this.audioAnalyser.frequencyBinCount);
console.log('π Audio analysis setup complete');
}
}
createParticleSystems() {
// Jog Wheel Particle Systems
this.createJogWheelParticles('A');
this.createJogWheelParticles('B');
// Waveform Particle Systems
this.createWaveformParticles('A');
this.createWaveformParticles('B');
// Mixer Particle System
this.createMixerParticles();
}
createJogWheelParticles(deck) {
const jogWheel = document.getElementById(`jogWheel${deck}`);
if (!jogWheel) return;
const rect = jogWheel.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const radius = rect.width / 2;
for (let i = 0; i < 50; i++) {
const angle = (i / 50) * Math.PI * 2;
const particle = {
x: centerX + Math.cos(angle) * radius * 0.8,
y: centerY + Math.sin(angle) * radius * 0.8,
vx: 0,
vy: 0,
life: 1.0,
maxLife: 1.0,
size: Math.random() * 3 + 1,
color: deck === 'A' ? '#e74c3c' : '#27ae60',
angle: angle,
radius: radius * 0.8,
speed: 0.02 + Math.random() * 0.03
};
this.particleSystems[`jogWheel${deck}`].push(particle);
}
}
createWaveformParticles(deck) {
const waveform = document.getElementById(`waveform${deck}`);
if (!waveform) return;
const rect = waveform.getBoundingClientRect();
for (let i = 0; i < 100; i++) {
const particle = {
x: rect.left + (i / 100) * rect.width,
y: rect.top + rect.height / 2,
vx: 0,
vy: 0,
life: 1.0,
maxLife: 1.0,
size: Math.random() * 2 + 1,
color: deck === 'A' ? '#e74c3c' : '#27ae60',
frequency: i,
amplitude: 0
};
this.particleSystems[`waveform${deck}`].push(particle);
}
}
createMixerParticles() {
const mixer = document.querySelector('.mixer');
if (!mixer) return;
const rect = mixer.getBoundingClientRect();
for (let i = 0; i < 30; i++) {
const particle = {
x: rect.left + Math.random() * rect.width,
y: rect.top + Math.random() * rect.height,
vx: (Math.random() - 0.5) * 2,
vy: (Math.random() - 0.5) * 2,
life: 1.0,
maxLife: 1.0,
size: Math.random() * 3 + 1,
color: '#f39c12'
};
this.particleSystems.mixer.push(particle);
}
}
updateParticles() {
// Update jog wheel particles
['A', 'B'].forEach(deck => {
this.updateJogWheelParticles(deck);
this.updateWaveformParticles(deck);
});
// Update mixer particles
this.updateMixerParticles();
// Get audio data if available
if (this.audioAnalyser && this.frequencyData) {
this.audioAnalyser.getByteFrequencyData(this.frequencyData);
this.applyAudioReactivity();
}
}
updateJogWheelParticles(deck) {
const particles = this.particleSystems[`jogWheel${deck}`];
const isPlaying = djInterface[`deck${deck}`].isPlaying;
particles.forEach(particle => {
// Rotate particles around jog wheel
particle.angle += particle.speed * (isPlaying ? 1 : 0.1);
const jogWheel = document.getElementById(`jogWheel${deck}`);
if (jogWheel) {
const rect = jogWheel.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
particle.x = centerX + Math.cos(particle.angle) * particle.radius;
particle.y = centerY + Math.sin(particle.angle) * particle.radius;
}
// Pulse based on audio
if (isPlaying) {
particle.size = Math.random() * 4 + 2;
particle.life = 1.0;
} else {
particle.size = Math.random() * 2 + 1;
particle.life *= 0.99;
}
});
}
updateWaveformParticles(deck) {
const particles = this.particleSystems[`waveform${deck}`];
const isPlaying = djInterface[`deck${deck}`].isPlaying;
particles.forEach((particle, index) => {
const waveform = document.getElementById(`waveform${deck}`);
if (!waveform) return;
const rect = waveform.getBoundingClientRect();
// Get frequency data for this particle
if (this.frequencyData && isPlaying) {
const freqIndex = Math.floor((index / particles.length) * this.frequencyData.length);
const amplitude = this.frequencyData[freqIndex] / 255;
particle.amplitude = amplitude;
// Update particle position based on frequency
particle.y = rect.top + (rect.height / 2) + (amplitude - 0.5) * rect.height * 0.8;
particle.size = amplitude * 4 + 1;
particle.life = 1.0;
} else {
particle.y = rect.top + rect.height / 2;
particle.size = 1;
particle.life *= 0.98;
}
particle.x = rect.left + (index / particles.length) * rect.width;
});
}
updateMixerParticles() {
const particles = this.particleSystems.mixer;
particles.forEach(particle => {
// Update position
particle.x += particle.vx;
particle.y += particle.vy;
// Bounce off boundaries
const mixer = document.querySelector('.mixer');
if (mixer) {
const rect = mixer.getBoundingClientRect();
if (particle.x < rect.left || particle.x > rect.right) {
particle.vx *= -1;
}
if (particle.y < rect.top || particle.y > rect.bottom) {
particle.vy *= -1;
}
}
// Fade out
particle.life *= 0.995;
// Respawn if dead
if (particle.life <= 0.1) {
particle.life = 1.0;
particle.x = Math.random() * window.innerWidth;
particle.y = Math.random() * window.innerHeight;
}
});
}
applyAudioReactivity() {
// Apply audio reactivity to all particle systems
const bassLevel = this.getBassLevel();
const midLevel = this.getMidLevel();
const trebleLevel = this.getTrebleLevel();
// Bass affects jog wheel intensity
['A', 'B'].forEach(deck => {
const particles = this.particleSystems[`jogWheel${deck}`];
particles.forEach(particle => {
particle.speed = 0.02 + (bassLevel * 0.1);
});
});
// Mid affects waveform intensity
['A', 'B'].forEach(deck => {
const particles = this.particleSystems[`waveform${deck}`];
particles.forEach(particle => {
particle.amplitude *= (1 + midLevel * 0.5);
});
});
// Treble affects mixer particles
const mixerParticles = this.particleSystems.mixer;
mixerParticles.forEach(particle => {
particle.size = Math.max(1, particle.size * (1 + trebleLevel * 0.3));
});
}
getBassLevel() {
if (!this.frequencyData) return 0;
const bassSum = this.frequencyData.slice(0, 8).reduce((a, b) => a + b, 0);
return bassSum / (8 * 255);
}
getMidLevel() {
if (!this.frequencyData) return 0;
const midSum = this.frequencyData.slice(8, 24).reduce((a, b) => a + b, 0);
return midSum / (16 * 255);
}
getTrebleLevel() {
if (!this.frequencyData) return 0;
const trebleSum = this.frequencyData.slice(24, 64).reduce((a, b) => a + b, 0);
return trebleSum / (40 * 255);
}
render() {
if (!this.ctx || !this.isActive) return;
// Clear canvas
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Render all particle systems
Object.values(this.particleSystems).flat().forEach(particle => {
if (particle.life > 0.1) {
this.ctx.save();
this.ctx.globalAlpha = particle.life;
this.ctx.fillStyle = particle.color;
this.ctx.beginPath();
this.ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.restore();
}
});
}
startParticleLoop() {
const animate = () => {
this.updateParticles();
this.render();
requestAnimationFrame(animate);
};
animate();
}
resizeCanvas() {
if (this.canvas) {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
}
}
activate() {
this.isActive = true;
console.log('π Particle Metaverse DJ System activated');
}
deactivate() {
this.isActive = false;
console.log('π Particle Metaverse DJ System deactivated');
}
}
// Initialize Particle Metaverse DJ System
const particleMetaverseDJ = new ParticleMetaverseDJ();
// Integrate with DJ Interface
const originalToggleDJInterface = djInterface.toggleDJInterface;
djInterface.toggleDJInterface = function() {
originalToggleDJInterface.call(this);
if (this.isActive) {
particleMetaverseDJ.activate();
} else {
particleMetaverseDJ.deactivate();
}
};
// Initialize when page loads
window.addEventListener('load', function() {
setTimeout(() => {
particleMetaverseDJ.init();
}, 2000);
});
// Handle window resize
window.addEventListener('resize', function() {
particleMetaverseDJ.resizeCanvas();
});
</script>