// AudiOhm Web App const API_BASE = 'http://192.168.1.204:8000/api/v1'; let authToken = localStorage.getItem('authToken') || null; let currentUser = JSON.parse(localStorage.getItem('currentUser')) || null; let currentTrack = null; let isPlaying = false; // DOM Elements (will be initialized on DOMContentLoaded) let audioPlayer, playBtn, progressBar, volumeBar; // API Helper Functions async function apiRequest(endpoint, options = {}) { const headers = { 'Content-Type': 'application/json', ...options.headers }; if (authToken) { headers['Authorization'] = `Bearer ${authToken}`; } try { const response = await fetch(`${API_BASE}${endpoint}`, { ...options, headers }); if (response.status === 401) { logout(); return null; } const data = await response.json(); return data; } catch (error) { console.error('API Error:', error); return null; } } // Auth Functions async function login(email, password) { const response = await fetch(`${API_BASE}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email, password: password }) }); if (response.ok) { const data = await response.json(); authToken = data.access_token; localStorage.setItem('authToken', authToken); // Get user info const user = await apiRequest('/auth/me'); if (user) { currentUser = user; localStorage.setItem('currentUser', JSON.stringify(user)); showMainApp(); } } else { const error = await response.json(); showError(error.detail || 'Email ou mot de passe incorrect'); } } async function register(username, email, password) { const response = await apiRequest('/auth/register', { method: 'POST', body: JSON.stringify({ username, email, password }) }); if (response) { showSuccess('Compte créé avec succès ! Vous pouvez maintenant vous connecter.'); showLoginForm(); } } function logout() { authToken = null; currentUser = null; localStorage.removeItem('authToken'); localStorage.removeItem('currentUser'); showLoginScreen(); } // UI Functions function showLoginScreen() { document.getElementById('loading-screen').classList.add('hidden'); document.getElementById('login-screen').classList.remove('hidden'); document.getElementById('main-app').classList.add('hidden'); document.getElementById('main-app').classList.remove('visible'); } function showMainApp() { document.getElementById('loading-screen').classList.add('hidden'); document.getElementById('login-screen').classList.add('hidden'); document.getElementById('main-app').classList.remove('hidden'); document.getElementById('main-app').classList.add('visible'); loadTrendingTracks(); loadPlaylists(); } function showLoginForm() { document.getElementById('login-form').classList.remove('hidden'); document.getElementById('register-form').classList.add('hidden'); } function showRegisterForm() { document.getElementById('login-form').classList.add('hidden'); document.getElementById('register-form').classList.remove('hidden'); } function showError(message) { const errorDiv = document.getElementById('auth-error'); errorDiv.textContent = message; errorDiv.classList.remove('hidden'); setTimeout(() => errorDiv.classList.add('hidden'), 5000); } function showSuccess(message) { alert(message); } // Music Functions async function loadTrendingTracks() { const tracks = await apiRequest('/music/trending?limit=10'); if (tracks) { displayTracks(tracks, 'trending-tracks'); } } async function searchTracks(query) { const results = await apiRequest(`/music/search?q=${encodeURIComponent(query)}`); if (results) { displayTracks(results.tracks || results, 'search-results'); } } function displayTracks(tracks, containerId) { const container = document.getElementById(containerId); if (!container) return; if (!tracks || tracks.length === 0) { container.innerHTML = '

Aucun résultat

'; return; } container.innerHTML = tracks.map(track => { const youtubeId = track.youtube_id; const coverUrl = track.image_url || track.thumbnail || 'https://via.placeholder.com/300x300/00F0FF/0A0E27?text=♪'; const artistName = track.artist_name || track.artist || 'Artiste inconnu'; // Store track data as JSON for playback const trackData = JSON.stringify({ title: track.title, artist_name: artistName, image_url: coverUrl, duration: track.duration }).replace(/"/g, '"'); return `
${track.title}
${track.title}
${artistName}
${formatDuration(track.duration)}
${youtubeId ? `` : 'Non disponible'}
`; }).join(''); } function playTrackFromCard(button, youtubeId) { // Get track data from the card element const card = button.closest('.track-card'); const trackDataJSON = card.getAttribute('data-track'); if (trackDataJSON) { // Parse the track data (convert " back to ") const trackData = JSON.parse(trackDataJSON.replace(/"/g, '"')); // Set current track with the data we have currentTrack = trackData; // Now call playTrack with the identifier playTrack(youtubeId, true); } else { playTrack(youtubeId, true); } } async function playTrack(identifier, isYoutubeId = true) { // identifier: track UUID or youtube_id // isYoutubeId: true if identifier is a youtube_id, false if it's a track UUID let streamUrl; let track; let shouldUpdateUI = false; if (isYoutubeId) { // Use YouTube streaming endpoint streamUrl = `${API_BASE}/music/youtube/${identifier}/stream`; // currentTrack should already be set by playTrackFromCard if (!currentTrack) { currentTrack = { title: 'Unknown Track', artist_name: 'Unknown Artist', image_url: null }; } track = currentTrack; shouldUpdateUI = true; } else { // Try UUID endpoint (for tracks in database) streamUrl = `${API_BASE}/music/tracks/${identifier}/stream`; // Get track details track = await apiRequest(`/music/tracks/${identifier}`); if (track) { currentTrack = track; shouldUpdateUI = true; } } if (track && shouldUpdateUI) { // Update player UI const coverUrl = track.image_url || track.thumbnail || '/static/img/default-cover.png'; document.getElementById('player-cover').src = coverUrl; document.getElementById('player-title').textContent = track.title; document.getElementById('player-artist').textContent = track.artist_name || track.artist || '-'; // Set audio source and play audioPlayer.src = streamUrl; audioPlayer.load(); // Important: load the source before playing audioPlayer.play().catch(e => { console.error('Playback error:', e); showError('Erreur lors de la lecture: ' + e.message); }); isPlaying = true; updatePlayButton(); } else if (currentTrack) { // Just update the source if UI is already set audioPlayer.src = streamUrl; audioPlayer.load(); audioPlayer.play().catch(e => { console.error('Playback error:', e); showError('Erreur lors de la lecture: ' + e.message); }); isPlaying = true; updatePlayButton(); } else { showError('Impossible de lire ce morceau'); } } function togglePlay() { if (!currentTrack) return; if (isPlaying) { audioPlayer.pause(); } else { audioPlayer.play(); } isPlaying = !isPlaying; updatePlayButton(); } function updatePlayButton() { playBtn.innerHTML = isPlaying ? '' : ''; } // Playlist Functions async function loadPlaylists() { const playlists = await apiRequest('/playlists'); if (playlists) { displayPlaylists(playlists); } } function displayPlaylists(playlists) { const container = document.getElementById('my-playlists'); if (!container) return; if (!playlists || playlists.length === 0) { container.innerHTML = '

Aucune playlist

'; return; } container.innerHTML = playlists.map(playlist => `
${playlist.name}
${playlist.name}
${playlist.track_count || 0} titres
`).join(''); } // Utility Functions function formatDuration(seconds) { if (!seconds) return '0:00'; const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, '0')}`; } // Navigation function navigateTo(page) { // Update nav items document.querySelectorAll('.nav-item').forEach(item => { item.classList.remove('active'); if (item.dataset.page === page) { item.classList.add('active'); } }); // Show/hide pages document.querySelectorAll('.page').forEach(p => { p.classList.remove('active'); }); document.getElementById(`${page}-page`).classList.add('active'); } // Event Listeners document.addEventListener('DOMContentLoaded', function() { // Initialize DOM Elements audioPlayer = document.getElementById('audio-player'); playBtn = document.getElementById('play-btn'); progressBar = document.getElementById('progress-bar'); volumeBar = document.getElementById('volume-bar'); // Check auth status if (authToken && currentUser) { showMainApp(); } else { showLoginScreen(); } // Login form document.getElementById('login-form').addEventListener('submit', function(e) { e.preventDefault(); const email = document.getElementById('login-email').value; const password = document.getElementById('login-password').value; login(email, password); }); // Register form document.getElementById('register-form').addEventListener('submit', function(e) { e.preventDefault(); const username = document.getElementById('register-username').value; const email = document.getElementById('register-email').value; const password = document.getElementById('register-password').value; register(username, email, password); }); // Show register form document.getElementById('show-register').addEventListener('click', function(e) { e.preventDefault(); showRegisterForm(); }); // Show login form document.getElementById('show-login').addEventListener('click', function(e) { e.preventDefault(); showLoginForm(); }); // Logout document.getElementById('logout-btn').addEventListener('click', logout); // Navigation document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', function(e) { e.preventDefault(); navigateTo(this.dataset.page); }); }); // Quick search document.getElementById('quick-search-btn').addEventListener('click', function() { const query = document.getElementById('quick-search').value; if (query) { navigateTo('search'); searchTracks(query); } }); // Search document.getElementById('search-btn').addEventListener('click', function() { const query = document.getElementById('search-input').value; if (query) { searchTracks(query); } }); // Player controls playBtn.addEventListener('click', togglePlay); // Audio events audioPlayer.addEventListener('loadedmetadata', function() { const duration = audioPlayer.duration; document.getElementById('total-time').textContent = formatDuration(Math.floor(duration)); }); audioPlayer.addEventListener('timeupdate', function() { const current = audioPlayer.currentTime; const duration = audioPlayer.duration; const progress = (current / duration) * 100; progressBar.value = progress; document.getElementById('current-time').textContent = formatDuration(Math.floor(current)); }); audioPlayer.addEventListener('ended', function() { isPlaying = false; updatePlayButton(); }); progressBar.addEventListener('input', function() { const duration = audioPlayer.duration; audioPlayer.currentTime = (this.value / 100) * duration; }); volumeBar.addEventListener('input', function() { audioPlayer.volume = this.value / 100; }); });