From 0c2b99223902ec52b7e689fb263e139ec43ce1a2 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:40:15 +0000 Subject: [PATCH 01/11] Initial plan From c1fa3558b1868c77173100abadce640f4e6b1e79 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:46:19 +0000 Subject: [PATCH 02/11] Add Spotify playlist migration feature with OAuth 2.0 PKCE authentication Agent-Logs-Url: https://github.com/Dcode9/MPLayer/sessions/ae99016d-fcf3-412a-a523-d734a0e0344a Co-authored-by: Dcode9 <194372792+Dcode9@users.noreply.github.com> --- SPOTIFY_SETUP.md | 210 ++++++++++++++++++++++++++++++++ home/css/styles.css | 4 + home/index.html | 47 ++++++- home/js/app.js | 16 ++- home/js/spotify-api.js | 264 ++++++++++++++++++++++++++++++++++++++++ home/js/spotify-auth.js | 207 +++++++++++++++++++++++++++++++ home/js/state.js | 15 ++- home/js/ui.js | 12 ++ home/js/views.js | 124 +++++++++++++++++++ 9 files changed, 892 insertions(+), 7 deletions(-) create mode 100644 SPOTIFY_SETUP.md create mode 100644 home/js/spotify-api.js create mode 100644 home/js/spotify-auth.js diff --git a/SPOTIFY_SETUP.md b/SPOTIFY_SETUP.md new file mode 100644 index 0000000..4607b37 --- /dev/null +++ b/SPOTIFY_SETUP.md @@ -0,0 +1,210 @@ +# Spotify Playlist Migration Setup Guide + +This guide will help you set up Spotify integration in MPLayer (D'Tunes) to import and play your Spotify playlists. + +## Features + +- **Sign in with Spotify**: Securely authenticate using OAuth 2.0 with PKCE (Proof Key for Code Exchange) +- **View All Playlists**: Browse all your Spotify playlists in the app +- **Playlist Migration**: Automatically convert Spotify playlists to playable tracks by finding matching songs on JioSaavn +- **Real-time Progress**: See conversion progress as tracks are matched +- **Persistent Authentication**: Stay signed in across sessions + +## Setup Instructions + +### Step 1: Create a Spotify App + +1. Go to the [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) +2. Log in with your Spotify account +3. Click **"Create app"** +4. Fill in the app details: + - **App name**: MPLayer (or any name you prefer) + - **App description**: Music player with Spotify integration + - **Website**: Your website URL (can be http://localhost for local development) + - **Redirect URI**: + - For local development: `http://localhost:8000/home/index.html` (adjust port if needed) + - For production: Your deployed URL (e.g., `https://yourdomain.com/home/index.html`) +5. Check the **"Web API"** option +6. Agree to the terms and click **"Save"** + +### Step 2: Get Your Client ID + +1. After creating the app, you'll see your **Client ID** on the app dashboard +2. Copy this Client ID (you'll need it in the next step) +3. **Important**: The Client ID is safe to use in frontend code. Do NOT use the Client Secret in a frontend app. + +### Step 3: Configure the App + +1. Open the file: `/home/js/spotify-auth.js` +2. Find line 8 where it says: + ```javascript + clientId: 'YOUR_SPOTIFY_CLIENT_ID', + ``` +3. Replace `'YOUR_SPOTIFY_CLIENT_ID'` with your actual Spotify Client ID: + ```javascript + clientId: 'abc123def456ghi789', // Your actual Client ID + ``` +4. Save the file + +### Step 4: Set the Redirect URI + +1. Make sure the `redirectUri` in `spotify-auth.js` matches your app's URL +2. By default, it's set to use the current page URL: + ```javascript + redirectUri: window.location.origin + window.location.pathname, + ``` +3. If you need to customize it, update this line accordingly + +### Step 5: Update Spotify App Settings + +1. Go back to your [Spotify App Dashboard](https://developer.spotify.com/dashboard) +2. Click on your app +3. Click **"Settings"** +4. Under **"Redirect URIs"**, add your app's URL (must exactly match what's in your code) + - Example: `http://localhost:8000/home/index.html` + - You can add multiple URIs for different environments (local, staging, production) +5. Click **"Add"** then **"Save"** + +## How to Use + +### Sign In + +1. Open MPLayer (D'Tunes) in your browser +2. In the left sidebar, scroll down to find the **"Connect Spotify"** button (green button with Spotify logo) +3. Click the button +4. You'll be redirected to Spotify to authorize the app +5. Grant the requested permissions: + - Read your playlists + - Read your saved tracks +6. You'll be redirected back to MPLayer, now signed in + +### View Your Playlists + +1. Click on **"Playlists"** in the left sidebar +2. Your Spotify playlists will appear at the top of the page +3. Each playlist shows: + - Playlist name + - Number of tracks + - Playlist owner + - Spotify badge + +### Play a Playlist + +1. Click on any Spotify playlist +2. The app will automatically: + - Fetch all tracks from the playlist + - Search for matching songs on JioSaavn + - Show a progress dialog + - Start playing the matched tracks +3. You'll see a success message showing how many tracks were matched +4. The matched tracks will start playing immediately + +### Sign Out + +1. Scroll down in the left sidebar +2. Click the **"Disconnect Spotify"** button +3. Your Spotify data will be cleared from the app + +## Technical Details + +### Authentication Flow + +- Uses **OAuth 2.0 Authorization Code Flow with PKCE** +- No client secret required (safe for frontend apps) +- Access tokens are stored in localStorage +- Tokens are automatically checked for expiration +- Session persists across page reloads + +### Playlist Conversion + +When you click on a Spotify playlist: + +1. The app fetches all tracks from the playlist (handles pagination for large playlists) +2. For each track, it searches JioSaavn using the track name and artist +3. The first matching result is selected +4. A progress bar shows the conversion status +5. All matched tracks are added to the queue and start playing + +**Note**: Not all Spotify tracks may be available on JioSaavn. The app shows you the match rate (e.g., "45 of 50 tracks matched"). + +### Data Storage + +The app stores the following in localStorage: + +- `spotify_access_token`: Your Spotify access token +- `spotify_token_expiry`: When the token expires +- `spotify_playlists`: Cached list of your playlists +- `spotify_user`: Your Spotify user profile + +All data is stored locally in your browser and is never sent to any server except Spotify's API. + +## Troubleshooting + +### "Spotify Client ID not configured" Error + +- Make sure you replaced `'YOUR_SPOTIFY_CLIENT_ID'` with your actual Client ID in `spotify-auth.js` +- Refresh the page after making changes + +### "Redirect URI mismatch" Error + +- The redirect URI in your code must EXACTLY match the one in your Spotify app settings +- Include the protocol (`http://` or `https://`) +- Include the port if using localhost (e.g., `:8000`) +- Include the full path (e.g., `/home/index.html`) +- Check for typos or extra spaces + +### "Authentication Failed" Error + +- Check your browser console for detailed error messages +- Make sure you granted all requested permissions +- Try signing out and signing in again +- Clear your browser's localStorage and try again + +### No Playlists Showing Up + +- Click the refresh button (circular arrow) next to "Spotify Playlists" +- Check your browser console for API errors +- Make sure you have playlists in your Spotify account +- Try signing out and signing in again + +### Low Match Rate + +- Spotify tracks may not be available on JioSaavn, especially for: + - International artists with limited distribution in India + - Very new releases + - Rare or obscure tracks +- Try playlists with popular Bollywood or Indian music for better results + +## Security Notes + +- **Never share your Spotify Client Secret** - This implementation uses PKCE which doesn't require a client secret +- The Client ID is safe to include in frontend code +- Access tokens are temporary (expire after 1 hour) +- All authentication is handled directly with Spotify - no intermediate servers + +## Privacy + +- Your Spotify credentials are never stored or accessed by this app +- Only the access token provided by Spotify is stored locally +- The app only requests read-only access to your playlists +- No data is sent to any server except Spotify's official API + +## Rate Limits + +- Spotify API has rate limits +- If you see "Rate limited" messages, wait a moment before trying again +- The app automatically handles rate limiting with retry logic + +## Support + +If you encounter issues: + +1. Check the browser console for error messages +2. Verify your Spotify app settings +3. Make sure your Client ID is correct +4. Try clearing localStorage and signing in again + +## Credits + +- Spotify Web API: https://developer.spotify.com/documentation/web-api +- OAuth 2.0 PKCE: https://oauth.net/2/pkce/ diff --git a/home/css/styles.css b/home/css/styles.css index 2647bca..290fa00 100644 --- a/home/css/styles.css +++ b/home/css/styles.css @@ -353,6 +353,10 @@ button.active-state svg { fill: rgba(62, 207, 142, 0.2); } background: rgba(139, 92, 246, 0.2); color: #a78bfa; } +.source-badge.spotify { + background: rgba(29, 185, 84, 0.2); + color: #1db954; +} /* --- Horizontal scroll sections --- */ .horizontal-scroll { diff --git a/home/index.html b/home/index.html index 3b38d21..cc86e01 100644 --- a/home/index.html +++ b/home/index.html @@ -68,6 +68,24 @@ + + +
+ + +
@@ -126,7 +144,32 @@

Top Albums

Playlists

- + + +
+
+

+ + + + Spotify Playlists +

+ +
+
+

Sign in with Spotify to see your playlists

+
+
+ +
+

Up Next (Queue)

@@ -227,6 +270,8 @@

Not Playing

+ + diff --git a/home/js/app.js b/home/js/app.js index d76cce1..f8b081c 100644 --- a/home/js/app.js +++ b/home/js/app.js @@ -81,7 +81,7 @@ document.addEventListener('keydown', (e) => { // ============================================ async function init() { debugLog('Initializing D\'Tunes Player...'); - + const vCanvas = document.getElementById('visualizer-canvas'); visualizerCtx = vCanvas.getContext('2d'); resizeVisualizer(); @@ -94,21 +94,27 @@ async function init() { // Load preferences preferences.load(); + // Initialize Spotify authentication + if (typeof spotifyAuth !== 'undefined') { + spotifyAuth.init(); + ui.updateSpotifyButton(); + } + // Initialize search searchManager.init(); // Load homepage content from JioSaavn homeView.load(); - + // Initialize UI listeners ui.initListeners(); - + // Start visualizer viz.startLoop(); - + // Render liked songs ui.renderLikedSongs(); - + // Restore last played track metadata in player (without auto-playing) try { const lastTrack = JSON.parse(localStorage.getItem('lastPlayedTrack')); diff --git a/home/js/spotify-api.js b/home/js/spotify-api.js new file mode 100644 index 0000000..7b9f375 --- /dev/null +++ b/home/js/spotify-api.js @@ -0,0 +1,264 @@ +// ============================================ +// SPOTIFY API SERVICE +// Handles all Spotify Web API interactions +// ============================================ + +const spotifyAPI = { + baseUrl: 'https://api.spotify.com/v1', + + // Make authenticated API request with retry + fetchWithAuth: async (endpoint, options = {}, retries = 3) => { + const token = spotifyAuth.getAccessToken(); + + if (!token) { + throw new Error('Not authenticated with Spotify'); + } + + for (let i = 0; i < retries; i++) { + try { + const url = endpoint.startsWith('http') ? endpoint : `${spotifyAPI.baseUrl}${endpoint}`; + const response = await fetch(url, { + ...options, + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + ...options.headers, + }, + }); + + if (response.status === 401) { + // Token expired + spotifyAuth.logout(); + throw new Error('Spotify session expired. Please sign in again.'); + } + + if (response.status === 429) { + // Rate limited + const retryAfter = response.headers.get('Retry-After') || 1; + debugLog(`Spotify: Rate limited, retrying after ${retryAfter}s`); + await new Promise(r => setTimeout(r, retryAfter * 1000)); + continue; + } + + if (!response.ok) { + throw new Error(`Spotify API error: ${response.status} ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + if (i === retries - 1) throw error; + await new Promise(r => setTimeout(r, 1000 * (i + 1))); + } + } + }, + + // Get current user's profile + getCurrentUser: async () => { + try { + const data = await spotifyAPI.fetchWithAuth('/me'); + return { + id: data.id, + name: data.display_name, + email: data.email, + img: data.images?.[0]?.url || '', + followers: data.followers?.total || 0, + country: data.country, + }; + } catch (error) { + errorHandler.handleApiError(error, 'Spotify getCurrentUser'); + return null; + } + }, + + // Get all user playlists (with pagination) + getAllPlaylists: async () => { + try { + const playlists = []; + let url = '/me/playlists?limit=50'; + + while (url) { + const data = await spotifyAPI.fetchWithAuth(url); + const normalized = data.items.map(spotifyAPI.normalizePlaylist); + playlists.push(...normalized); + + // Check for next page + url = data.next; + } + + debugLog(`Spotify: Fetched ${playlists.length} playlists`); + return playlists; + } catch (error) { + errorHandler.handleApiError(error, 'Spotify getAllPlaylists'); + return []; + } + }, + + // Get playlist tracks + getPlaylistTracks: async (playlistId) => { + try { + const tracks = []; + let url = `/playlists/${playlistId}/tracks?limit=100`; + + while (url) { + const data = await spotifyAPI.fetchWithAuth(url); + + // Filter out null tracks (deleted/unavailable songs) + const validTracks = data.items + .filter(item => item.track && item.track.id) + .map(item => spotifyAPI.normalizeTrack(item.track)); + + tracks.push(...validTracks); + + // Check for next page + url = data.next; + } + + debugLog(`Spotify: Fetched ${tracks.length} tracks from playlist ${playlistId}`); + return tracks; + } catch (error) { + errorHandler.handleApiError(error, 'Spotify getPlaylistTracks'); + return []; + } + }, + + // Get user's saved tracks (liked songs) + getSavedTracks: async () => { + try { + const tracks = []; + let url = '/me/tracks?limit=50'; + + while (url) { + const data = await spotifyAPI.fetchWithAuth(url); + + const validTracks = data.items + .filter(item => item.track && item.track.id) + .map(item => spotifyAPI.normalizeTrack(item.track)); + + tracks.push(...validTracks); + + // Check for next page + url = data.next; + } + + debugLog(`Spotify: Fetched ${tracks.length} saved tracks`); + return tracks; + } catch (error) { + errorHandler.handleApiError(error, 'Spotify getSavedTracks'); + return []; + } + }, + + // Search tracks on Spotify (for matching with JioSaavn) + searchTracks: async (query, limit = 20) => { + try { + const data = await spotifyAPI.fetchWithAuth( + `/search?q=${encodeURIComponent(query)}&type=track&limit=${limit}` + ); + return data.tracks.items.map(spotifyAPI.normalizeTrack); + } catch (error) { + errorHandler.handleApiError(error, 'Spotify searchTracks'); + return []; + } + }, + + // Normalize playlist object + normalizePlaylist: (playlist) => { + if (!playlist) return null; + return { + id: playlist.id, + name: playlist.name, + description: playlist.description || '', + img: playlist.images?.[0]?.url || 'https://placehold.co/300/333/fff?text=Playlist', + trackCount: playlist.tracks?.total || 0, + owner: playlist.owner?.display_name || 'Unknown', + isPublic: playlist.public, + isCollaborative: playlist.collaborative, + source: 'spotify', + }; + }, + + // Normalize track object to match app's format + normalizeTrack: (track) => { + if (!track) return null; + return { + id: `spotify_${track.id}`, // Prefix to distinguish from JioSaavn + spotifyId: track.id, // Keep original ID for Spotify operations + name: track.name, + artist: track.artists.map(a => a.name).join(', '), + artistIds: track.artists.map(a => a.id), + album: track.album.name, + albumId: track.album.id, + img: track.album.images?.[0]?.url || 'https://placehold.co/300/333/fff?text=Music', + url: track.preview_url, // Spotify only provides 30s previews for web API + duration: Math.floor(track.duration_ms / 1000), + year: track.album.release_date?.substring(0, 4) || '', + source: 'spotify', + isPreview: true, // Mark as preview since we can't stream full tracks + spotifyUri: track.uri, // Keep URI for potential future use + externalUrl: track.external_urls?.spotify || '', + }; + }, + + // Try to find matching JioSaavn track for a Spotify track + findJioSaavnMatch: async (spotifyTrack) => { + try { + // Search JioSaavn with track name and artist + const query = `${spotifyTrack.name} ${spotifyTrack.artist}`; + const results = await jiosaavnAPI.searchSongs(query, 5); + + if (results.length === 0) { + return null; + } + + // Simple matching: return the first result + // Could be improved with fuzzy matching or similarity scoring + return results[0]; + } catch (error) { + debugLog('Error finding JioSaavn match:', error); + return null; + } + }, + + // Convert Spotify playlist to playable JioSaavn tracks + convertPlaylistToJioSaavn: async (spotifyPlaylistId, onProgress) => { + try { + const spotifyTracks = await spotifyAPI.getPlaylistTracks(spotifyPlaylistId); + const jiosaavnTracks = []; + let matched = 0; + + for (let i = 0; i < spotifyTracks.length; i++) { + const spotifyTrack = spotifyTracks[i]; + + // Report progress + if (onProgress) { + onProgress({ + current: i + 1, + total: spotifyTracks.length, + matched: matched, + }); + } + + // Try to find matching JioSaavn track + const jiosaavnTrack = await spotifyAPI.findJioSaavnMatch(spotifyTrack); + + if (jiosaavnTrack) { + jiosaavnTracks.push(jiosaavnTrack); + matched++; + } + + // Add small delay to avoid overwhelming the API + await new Promise(r => setTimeout(r, 100)); + } + + debugLog(`Spotify: Converted ${matched}/${spotifyTracks.length} tracks to JioSaavn`); + return { + tracks: jiosaavnTracks, + total: spotifyTracks.length, + matched: matched, + }; + } catch (error) { + errorHandler.handleApiError(error, 'Spotify convertPlaylistToJioSaavn'); + return { tracks: [], total: 0, matched: 0 }; + } + }, +}; diff --git a/home/js/spotify-auth.js b/home/js/spotify-auth.js new file mode 100644 index 0000000..23509d7 --- /dev/null +++ b/home/js/spotify-auth.js @@ -0,0 +1,207 @@ +// ============================================ +// SPOTIFY AUTHENTICATION MODULE +// Implements OAuth 2.0 with PKCE (Proof Key for Code Exchange) +// ============================================ + +const spotifyAuth = { + // Spotify App Credentials (REPLACE WITH YOUR OWN) + clientId: 'YOUR_SPOTIFY_CLIENT_ID', // Replace with your Spotify Client ID + redirectUri: window.location.origin + window.location.pathname, + scopes: [ + 'playlist-read-private', + 'playlist-read-collaborative', + 'user-library-read' + ].join(' '), + + // State management + isAuthenticated: false, + accessToken: null, + tokenExpiry: null, + + // Initialize authentication state from localStorage + init: () => { + const token = localStorage.getItem('spotify_access_token'); + const expiry = localStorage.getItem('spotify_token_expiry'); + + if (token && expiry) { + const expiryTime = parseInt(expiry); + if (Date.now() < expiryTime) { + spotifyAuth.accessToken = token; + spotifyAuth.tokenExpiry = expiryTime; + spotifyAuth.isAuthenticated = true; + debugLog('Spotify: Restored authentication from localStorage'); + return true; + } else { + debugLog('Spotify: Token expired, clearing localStorage'); + spotifyAuth.logout(); + } + } + + // Check for OAuth callback + spotifyAuth.handleCallback(); + return spotifyAuth.isAuthenticated; + }, + + // Generate random string for PKCE + generateRandomString: (length) => { + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const values = crypto.getRandomValues(new Uint8Array(length)); + return values.reduce((acc, x) => acc + possible[x % possible.length], ''); + }, + + // Generate code challenge for PKCE + generateCodeChallenge: async (codeVerifier) => { + const digest = await crypto.subtle.digest( + 'SHA-256', + new TextEncoder().encode(codeVerifier) + ); + return btoa(String.fromCharCode(...new Uint8Array(digest))) + .replace(/=/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); + }, + + // Start OAuth flow + login: async () => { + if (spotifyAuth.clientId === 'YOUR_SPOTIFY_CLIENT_ID') { + errorHandler.show('Spotify Client ID not configured. Please add your Client ID in spotify-auth.js', 6000); + return; + } + + const codeVerifier = spotifyAuth.generateRandomString(64); + const codeChallenge = await spotifyAuth.generateCodeChallenge(codeVerifier); + + // Store code verifier for later use + localStorage.setItem('spotify_code_verifier', codeVerifier); + + // Build authorization URL + const params = new URLSearchParams({ + client_id: spotifyAuth.clientId, + response_type: 'code', + redirect_uri: spotifyAuth.redirectUri, + scope: spotifyAuth.scopes, + code_challenge_method: 'S256', + code_challenge: codeChallenge, + }); + + const authUrl = `https://accounts.spotify.com/authorize?${params.toString()}`; + debugLog('Spotify: Redirecting to authorization URL'); + window.location.href = authUrl; + }, + + // Handle OAuth callback + handleCallback: async () => { + const params = new URLSearchParams(window.location.search); + const code = params.get('code'); + const error = params.get('error'); + + if (error) { + errorHandler.show(`Spotify authorization failed: ${error}`); + // Clean up URL + window.history.replaceState({}, document.title, window.location.pathname); + return; + } + + if (code) { + debugLog('Spotify: Authorization code received, exchanging for token'); + const codeVerifier = localStorage.getItem('spotify_code_verifier'); + + if (!codeVerifier) { + errorHandler.show('Spotify authentication error: Code verifier not found'); + window.history.replaceState({}, document.title, window.location.pathname); + return; + } + + try { + // Exchange code for access token + const response = await fetch('https://accounts.spotify.com/api/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + client_id: spotifyAuth.clientId, + grant_type: 'authorization_code', + code: code, + redirect_uri: spotifyAuth.redirectUri, + code_verifier: codeVerifier, + }), + }); + + if (!response.ok) { + throw new Error(`Token exchange failed: ${response.status}`); + } + + const data = await response.json(); + + // Store token + spotifyAuth.accessToken = data.access_token; + spotifyAuth.tokenExpiry = Date.now() + (data.expires_in * 1000); + spotifyAuth.isAuthenticated = true; + + localStorage.setItem('spotify_access_token', data.access_token); + localStorage.setItem('spotify_token_expiry', spotifyAuth.tokenExpiry.toString()); + localStorage.removeItem('spotify_code_verifier'); + + debugLog('Spotify: Authentication successful'); + errorHandler.show('Successfully connected to Spotify!', 3000); + + // Clean up URL and reload to update UI + window.history.replaceState({}, document.title, window.location.pathname); + + // Update UI + if (typeof ui !== 'undefined' && ui.updateSpotifyButton) { + ui.updateSpotifyButton(); + } + + // Auto-load playlists if we're on the playlists view + if (typeof router !== 'undefined' && router.currentView === 'playlists') { + if (typeof playlistsView !== 'undefined' && playlistsView.loadSpotifyPlaylists) { + playlistsView.loadSpotifyPlaylists(); + } + } + + } catch (error) { + debugError('Spotify token exchange error:', error); + errorHandler.show('Failed to connect to Spotify. Please try again.'); + localStorage.removeItem('spotify_code_verifier'); + window.history.replaceState({}, document.title, window.location.pathname); + } + } + }, + + // Logout and clear tokens + logout: () => { + spotifyAuth.accessToken = null; + spotifyAuth.tokenExpiry = null; + spotifyAuth.isAuthenticated = false; + + localStorage.removeItem('spotify_access_token'); + localStorage.removeItem('spotify_token_expiry'); + localStorage.removeItem('spotify_code_verifier'); + localStorage.removeItem('spotify_playlists'); + + debugLog('Spotify: Logged out'); + + // Update UI + if (typeof ui !== 'undefined' && ui.updateSpotifyButton) { + ui.updateSpotifyButton(); + } + }, + + // Check if token is still valid + isTokenValid: () => { + if (!spotifyAuth.accessToken || !spotifyAuth.tokenExpiry) { + return false; + } + return Date.now() < spotifyAuth.tokenExpiry; + }, + + // Get valid access token + getAccessToken: () => { + if (spotifyAuth.isTokenValid()) { + return spotifyAuth.accessToken; + } + return null; + } +}; diff --git a/home/js/state.js b/home/js/state.js index 589c6dc..8f7915e 100644 --- a/home/js/state.js +++ b/home/js/state.js @@ -37,7 +37,20 @@ const state = { } catch(e) { return {}; } })(), isLoading: false, - searchDebounce: null + searchDebounce: null, + // Spotify state + spotifyPlaylists: (() => { + try { + const data = JSON.parse(localStorage.getItem('spotify_playlists') || '[]'); + return Array.isArray(data) ? data : []; + } catch(e) { return []; } + })(), + spotifyUser: (() => { + try { + const data = JSON.parse(localStorage.getItem('spotify_user') || 'null'); + return data; + } catch(e) { return null; } + })() }; // ============================================ diff --git a/home/js/ui.js b/home/js/ui.js index 1e44b1c..b9b6b4e 100644 --- a/home/js/ui.js +++ b/home/js/ui.js @@ -284,5 +284,17 @@ const ui = { const id = state.currentTrack.id; const btn = document.getElementById('p-like-btn'); if(btn) btn.className = state.likedIds.includes(id) ? 'text-red-500 transition ml-2' : 'text-gray-400 hover:text-red-500 transition ml-2'; + }, + updateSpotifyButton: () => { + const signinBtn = document.getElementById('spotify-signin-btn'); + const signoutBtn = document.getElementById('spotify-signout-btn'); + + if (typeof spotifyAuth !== 'undefined' && spotifyAuth.isAuthenticated) { + if (signinBtn) signinBtn.classList.add('hidden'); + if (signoutBtn) signoutBtn.classList.remove('hidden'); + } else { + if (signinBtn) signinBtn.classList.remove('hidden'); + if (signoutBtn) signoutBtn.classList.add('hidden'); + } } }; diff --git a/home/js/views.js b/home/js/views.js index 964fb9a..ffe9c4d 100644 --- a/home/js/views.js +++ b/home/js/views.js @@ -25,7 +25,10 @@ const router = { if (view === 'playlists') { ui.renderLikedSongs(); ui.renderQueue(); + playlistsView.load(); } + + router.currentView = view; } }; @@ -266,3 +269,124 @@ const albumsView = { } } }; + +// ============================================ +// PLAYLISTS VIEW (WITH SPOTIFY INTEGRATION) +// ============================================ +const playlistsView = { + loaded: false, + + load: () => { + if (typeof spotifyAuth !== 'undefined' && spotifyAuth.isAuthenticated) { + playlistsView.loadSpotifyPlaylists(); + } + }, + + loadSpotifyPlaylists: async () => { + const container = document.getElementById('spotify-playlists-container'); + if (!container) return; + + container.innerHTML = '
Loading Spotify playlists...
'; + + try { + // Fetch user info + const user = await spotifyAPI.getCurrentUser(); + if (user) { + state.spotifyUser = user; + localStorage.setItem('spotify_user', JSON.stringify(user)); + } + + // Fetch playlists + const playlists = await spotifyAPI.getAllPlaylists(); + state.spotifyPlaylists = playlists; + localStorage.setItem('spotify_playlists', JSON.stringify(playlists)); + + if (playlists.length === 0) { + container.innerHTML = '

No playlists found

'; + return; + } + + // Render playlists + container.innerHTML = ` +
+ ${playlists.map(playlist => ` +
+
+ +
+ + + +
+
+

${searchManager.escapeHtml(playlist.name)}

+

${playlist.trackCount} tracks • ${searchManager.escapeHtml(playlist.owner)}

+ Spotify +
+ `).join('')} +
+ `; + + } catch (error) { + debugError('Error loading Spotify playlists:', error); + container.innerHTML = '

Unable to load Spotify playlists

'; + } + }, + + openSpotifyPlaylist: async (playlistId) => { + const container = document.getElementById('spotify-playlists-container'); + if (!container) return; + + // Show loading overlay + const overlay = document.createElement('div'); + overlay.id = 'playlist-loading-overlay'; + overlay.className = 'fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-[100]'; + overlay.innerHTML = ` +
+

Converting Playlist

+

Finding matching tracks on JioSaavn...

+
+
+
+
+
+

Starting...

+
+ `; + document.body.appendChild(overlay); + + try { + // Convert Spotify playlist to JioSaavn tracks + const result = await spotifyAPI.convertPlaylistToJioSaavn(playlistId, (progress) => { + const progressBar = document.getElementById('convert-progress'); + const statusText = document.getElementById('convert-status'); + + if (progressBar && statusText) { + const percentage = Math.round((progress.current / progress.total) * 100); + progressBar.style.width = `${percentage}%`; + statusText.textContent = `Processing ${progress.current} of ${progress.total} tracks (${progress.matched} matched)`; + } + }); + + // Remove overlay + overlay.remove(); + + if (result.tracks.length === 0) { + errorHandler.show('Could not find any matching tracks on JioSaavn', 4000); + return; + } + + // Play the converted tracks + player.setQueue(result.tracks, 0); + + // Show success message + const matchRate = Math.round((result.matched / result.total) * 100); + errorHandler.show(`Successfully converted ${result.matched} of ${result.total} tracks (${matchRate}%)`, 5000); + + } catch (error) { + debugError('Error converting Spotify playlist:', error); + overlay.remove(); + errorHandler.show('Failed to convert playlist. Please try again.'); + } + } +}; From 8ca30f1d242f1061acaa246d0bb52c2046e989d5 Mon Sep 17 00:00:00 2001 From: Dhairya Shah Date: Tue, 31 Mar 2026 19:29:36 +0530 Subject: [PATCH 03/11] Update spotify-auth.js --- home/js/spotify-auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home/js/spotify-auth.js b/home/js/spotify-auth.js index 23509d7..fed3292 100644 --- a/home/js/spotify-auth.js +++ b/home/js/spotify-auth.js @@ -5,7 +5,7 @@ const spotifyAuth = { // Spotify App Credentials (REPLACE WITH YOUR OWN) - clientId: 'YOUR_SPOTIFY_CLIENT_ID', // Replace with your Spotify Client ID + clientId: '8fba37005d964e2599ce567c69ee7f1d', // Replace with your Spotify Client ID redirectUri: window.location.origin + window.location.pathname, scopes: [ 'playlist-read-private', From 7b8511256c630ecc8a8bb3af0ba9dbd3da3fffce Mon Sep 17 00:00:00 2001 From: Dhairya Shah Date: Tue, 31 Mar 2026 19:32:15 +0530 Subject: [PATCH 04/11] Update redirect URI for Spotify authentication --- home/js/spotify-auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home/js/spotify-auth.js b/home/js/spotify-auth.js index fed3292..266b1ad 100644 --- a/home/js/spotify-auth.js +++ b/home/js/spotify-auth.js @@ -6,7 +6,7 @@ const spotifyAuth = { // Spotify App Credentials (REPLACE WITH YOUR OWN) clientId: '8fba37005d964e2599ce567c69ee7f1d', // Replace with your Spotify Client ID - redirectUri: window.location.origin + window.location.pathname, + redirectUri: 'https://play.dverse.fun/home/index.html', scopes: [ 'playlist-read-private', 'playlist-read-collaborative', From 99f66596f4fac7950c021ed1512739f278e4c1b8 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:08:21 +0000 Subject: [PATCH 05/11] Fix Spotify OAuth: replace deprecated Implicit Grant with PKCE Authorization Code Flow Agent-Logs-Url: https://github.com/Dcode9/MPLayer/sessions/52ec8d8b-bde4-4316-ba32-6ce3d90d9196 Co-authored-by: Dcode9 <194372792+Dcode9@users.noreply.github.com> --- index.html | 86 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/index.html b/index.html index 4378be6..16ce6c5 100644 --- a/index.html +++ b/index.html @@ -745,30 +745,94 @@

Audio Quality

}; // ============================================ - // SPOTIFY API INTEGRATION + // SPOTIFY API INTEGRATION (OAuth 2.0 with PKCE) // ============================================ const spotifyManager = { clientId: '8fba37005d964e2599ce567c69ee7f1d', // 🔴 ADD YOUR SPOTIFY CLIENT ID HERE redirectUri: window.location.href.split('#')[0].split('?')[0], token: null, - login: () => { + // Generate random string for PKCE + generateRandomString: (length) => { + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const values = crypto.getRandomValues(new Uint8Array(length)); + return values.reduce((acc, x) => acc + possible[x % possible.length], ''); + }, + + // Generate code challenge for PKCE + generateCodeChallenge: async (codeVerifier) => { + const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier)); + return btoa(String.fromCharCode(...new Uint8Array(digest))) + .replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); + }, + + login: async () => { if(!spotifyManager.clientId) { alert("Developer setup required: Please open the HTML file and add your Spotify Client ID to the 'spotifyManager.clientId' variable."); return; } + + const codeVerifier = spotifyManager.generateRandomString(64); + const codeChallenge = await spotifyManager.generateCodeChallenge(codeVerifier); + + localStorage.setItem('spotify_code_verifier', codeVerifier); + const scopes = 'playlist-read-private playlist-read-collaborative'; - const authUrl = `https://accounts.spotify.com/authorize?client_id=${spotifyManager.clientId}&response_type=token&redirect_uri=${encodeURIComponent(spotifyManager.redirectUri)}&scope=${encodeURIComponent(scopes)}`; + const authUrl = `https://accounts.spotify.com/authorize?client_id=${spotifyManager.clientId}&response_type=code&redirect_uri=${encodeURIComponent(spotifyManager.redirectUri)}&scope=${encodeURIComponent(scopes)}&code_challenge_method=S256&code_challenge=${codeChallenge}`; window.location.href = authUrl; }, - - checkToken: () => { - const hash = window.location.hash; - if (hash && hash.includes('access_token=')) { - const params = new URLSearchParams(hash.substring(1)); - spotifyManager.token = params.get('access_token'); - window.location.hash = ''; - setTimeout(() => ui.toggleSpotifyModal(true), 500); + + checkToken: async () => { + // Check if we have a stored token + const storedToken = localStorage.getItem('spotify_access_token'); + const tokenExpiry = localStorage.getItem('spotify_token_expiry'); + + if (storedToken && tokenExpiry && Date.now() < parseInt(tokenExpiry)) { + spotifyManager.token = storedToken; + setTimeout(() => ui.toggleSpotifyModal(true), 500); + return; + } + + // Check for OAuth callback with authorization code + const params = new URLSearchParams(window.location.search); + const code = params.get('code'); + + if (code) { + const codeVerifier = localStorage.getItem('spotify_code_verifier'); + + if (codeVerifier) { + try { + const response = await fetch('https://accounts.spotify.com/api/token', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + client_id: spotifyManager.clientId, + grant_type: 'authorization_code', + code: code, + redirect_uri: spotifyManager.redirectUri, + code_verifier: codeVerifier, + }) + }); + + if (response.ok) { + const data = await response.json(); + spotifyManager.token = data.access_token; + + // Store token with expiry + localStorage.setItem('spotify_access_token', data.access_token); + localStorage.setItem('spotify_token_expiry', (Date.now() + data.expires_in * 1000).toString()); + localStorage.removeItem('spotify_code_verifier'); + + // Clean up URL + window.history.replaceState({}, document.title, window.location.pathname); + + setTimeout(() => ui.toggleSpotifyModal(true), 500); + } + } catch (e) { + console.error('Token exchange failed:', e); + localStorage.removeItem('spotify_code_verifier'); + } + } } }, From 1188a94deb6324ee642a4e8837a5b265f673ad49 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:16:29 +0000 Subject: [PATCH 06/11] Update redirect URI for Spotify authentication Agent-Logs-Url: https://github.com/Dcode9/MPLayer/sessions/f1e4f31c-b0f2-42ee-97d1-a75fa2dcf714 Co-authored-by: Dcode9 <194372792+Dcode9@users.noreply.github.com> --- SPOTIFY_REDIRECT_URI_SETUP.md | 123 ++++++++++++++++++++++++++++++++++ index.html | 2 +- 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 SPOTIFY_REDIRECT_URI_SETUP.md diff --git a/SPOTIFY_REDIRECT_URI_SETUP.md b/SPOTIFY_REDIRECT_URI_SETUP.md new file mode 100644 index 0000000..1dae3de --- /dev/null +++ b/SPOTIFY_REDIRECT_URI_SETUP.md @@ -0,0 +1,123 @@ +# Spotify Redirect URI Configuration + +## Issue: "redirect_uri: Not matching configuration" + +This error occurs when the redirect URI sent in the OAuth request doesn't match exactly what's configured in your Spotify Developer Dashboard. + +## Current Redirect URIs + +The application uses these redirect URIs: + +1. **Root application** (`/index.html`): `https://play.dverse.fun/index.html` +2. **Home application** (`/home/index.html`): `https://play.dverse.fun/home/index.html` + +## How to Configure in Spotify Developer Dashboard + +### Step 1: Access Your Spotify App Settings + +1. Go to [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) +2. Click on your app (or create a new one) +3. Click on **"Settings"** button + +### Step 2: Add Redirect URIs + +In the **Redirect URIs** section, add **BOTH** of these URIs **EXACTLY** as shown: + +``` +https://play.dverse.fun/index.html +https://play.dverse.fun/home/index.html +``` + +**Important Notes:** + +- ✅ **Include the full path** including the HTML file name +- ✅ **Use HTTPS** (not HTTP) for production +- ✅ **No trailing slashes** +- ✅ **Match the protocol exactly** (https:// vs http://) +- ✅ **Match the domain exactly** (including subdomains) +- ✅ **Match the path exactly** (case-sensitive) + +### Step 3: Save Changes + +1. Click **"Add"** after entering each URI +2. Click **"Save"** at the bottom of the page +3. Wait a few seconds for changes to propagate + +### For Local Development + +If you're testing locally, also add these URIs: + +``` +http://localhost:8000/index.html +http://localhost:8000/home/index.html +``` + +Or adjust the port number if you're using a different local server port. + +## Troubleshooting + +### Still Getting "redirect_uri: Not matching configuration"? + +1. **Check for typos**: The URI must match EXACTLY character-by-character +2. **Check protocol**: Make sure you're using `https://` not `http://` (or vice versa) +3. **Check trailing slashes**: The URIs should NOT have trailing slashes +4. **Check path**: Make sure `/index.html` or `/home/index.html` is included +5. **Wait**: Sometimes it takes a minute for Spotify to sync changes +6. **Clear cache**: Try clearing browser cache and localStorage +7. **Check URL in browser**: Make sure you're actually accessing the URL that matches the redirect URI + +### Common Mistakes + +❌ `https://play.dverse.fun` (missing the HTML file) +❌ `https://play.dverse.fun/` (trailing slash) +❌ `http://play.dverse.fun/index.html` (wrong protocol) +❌ `https://play.dverse.fun/Index.html` (wrong case) +❌ `https://play.dverse.fun/home` (missing the HTML file) + +✅ `https://play.dverse.fun/index.html` (correct) +✅ `https://play.dverse.fun/home/index.html` (correct) + +## Updating Redirect URI in Code + +If you need to change the redirect URI (e.g., for a different domain): + +### For Root Application (`/index.html`) + +Edit line 752 in `/index.html`: + +```javascript +redirectUri: 'https://your-domain.com/index.html', +``` + +### For Home Application (`/home/index.html`) + +Edit line 9 in `/home/js/spotify-auth.js`: + +```javascript +redirectUri: 'https://your-domain.com/home/index.html', +``` + +**Important**: After changing the redirect URI in code, you MUST also update it in your Spotify Developer Dashboard! + +## Why This Error Happens + +Spotify requires the redirect URI to match EXACTLY for security reasons. This prevents attackers from: +- Intercepting OAuth tokens +- Redirecting users to malicious sites +- Stealing user credentials + +The redirect URI acts as a security whitelist - only URLs you explicitly approve can receive the OAuth response. + +## Related Files + +- `/index.html` - Line 752 (spotifyManager.redirectUri) +- `/home/js/spotify-auth.js` - Line 9 (spotifyAuth.redirectUri) +- `SPOTIFY_SETUP.md` - General Spotify setup documentation + +## Support + +If you continue to have issues: +1. Double-check all URIs match exactly +2. Try removing and re-adding the URIs in Spotify Dashboard +3. Verify your app is using the correct Client ID +4. Check browser console for additional error messages diff --git a/index.html b/index.html index 16ce6c5..b90a026 100644 --- a/index.html +++ b/index.html @@ -749,7 +749,7 @@

Audio Quality

// ============================================ const spotifyManager = { clientId: '8fba37005d964e2599ce567c69ee7f1d', // 🔴 ADD YOUR SPOTIFY CLIENT ID HERE - redirectUri: window.location.href.split('#')[0].split('?')[0], + redirectUri: 'https://play.dverse.fun/index.html', token: null, // Generate random string for PKCE From 06f8a3dbd75b408adf4f1d00b30ff16cddf09908 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Tue, 31 Mar 2026 16:05:18 +0000 Subject: [PATCH 07/11] Enable debug mode and add comprehensive logging for Spotify playlist loading Agent-Logs-Url: https://github.com/Dcode9/MPLayer/sessions/c4383a86-79a9-419c-ac02-37c7f9a8f441 Co-authored-by: Dcode9 <194372792+Dcode9@users.noreply.github.com> --- home/js/config.js | 2 +- home/js/views.js | 58 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/home/js/config.js b/home/js/config.js index 5116be4..f6d20f6 100644 --- a/home/js/config.js +++ b/home/js/config.js @@ -25,7 +25,7 @@ function switchToNextApi() { // ============================================ // DEBUG LOGGING // ============================================ -const DEBUG = false; // Set to true for development +const DEBUG = true; // Set to true for development function debugLog(...args) { if (DEBUG) console.log('[JioSaavn Player]', ...args); } diff --git a/home/js/views.js b/home/js/views.js index ffe9c4d..5301fbb 100644 --- a/home/js/views.js +++ b/home/js/views.js @@ -2,20 +2,23 @@ // ROUTER // ============================================ const router = { + currentView: 'home', // Initialize with default view go: (view) => { + debugLog(`Router: Navigating to ${view}`); + document.querySelectorAll('.view-section').forEach(el => el.classList.remove('active')); const target = document.getElementById('view-'+view); if(target) target.classList.add('active'); - + document.querySelectorAll('.nav-btn').forEach(el => el.classList.remove('active')); const btns = document.querySelectorAll('.nav-btn'); - + const viewMap = { 'home': 0, 'trending': 1, 'albums': 2, 'playlists': 3, 'album-detail': 2, 'search': -1 }; const btnIdx = viewMap[view]; if (btnIdx >= 0 && btns[btnIdx]) { btns[btnIdx].classList.add('active'); } - + if (view === 'trending' && !trendingView.loaded) { trendingView.load(); } @@ -23,12 +26,14 @@ const router = { albumsView.load(); } if (view === 'playlists') { + debugLog('Playlists view activated'); ui.renderLikedSongs(); ui.renderQueue(); playlistsView.load(); } router.currentView = view; + debugLog(`Router: Current view set to ${router.currentView}`); } }; @@ -277,30 +282,56 @@ const playlistsView = { loaded: false, load: () => { + debugLog('playlistsView.load() called'); + debugLog('Spotify auth status:', { + isDefined: typeof spotifyAuth !== 'undefined', + isAuthenticated: typeof spotifyAuth !== 'undefined' ? spotifyAuth.isAuthenticated : false, + hasToken: typeof spotifyAuth !== 'undefined' ? !!spotifyAuth.getAccessToken() : false + }); + if (typeof spotifyAuth !== 'undefined' && spotifyAuth.isAuthenticated) { + debugLog('User is authenticated, loading Spotify playlists...'); playlistsView.loadSpotifyPlaylists(); + } else { + debugLog('User is not authenticated with Spotify'); } }, loadSpotifyPlaylists: async () => { const container = document.getElementById('spotify-playlists-container'); - if (!container) return; + if (!container) { + debugError('Spotify playlists container not found!'); + return; + } + debugLog('Loading Spotify playlists...'); container.innerHTML = '
Loading Spotify playlists...
'; try { + // Verify authentication + if (!spotifyAuth.isAuthenticated || !spotifyAuth.getAccessToken()) { + debugError('Not authenticated with Spotify'); + container.innerHTML = '

Please sign in with Spotify to see your playlists

'; + return; + } + + debugLog('Fetching user info...'); // Fetch user info const user = await spotifyAPI.getCurrentUser(); if (user) { state.spotifyUser = user; localStorage.setItem('spotify_user', JSON.stringify(user)); + debugLog('User info fetched:', user.name); } + debugLog('Fetching playlists...'); // Fetch playlists const playlists = await spotifyAPI.getAllPlaylists(); state.spotifyPlaylists = playlists; localStorage.setItem('spotify_playlists', JSON.stringify(playlists)); + debugLog(`Fetched ${playlists.length} playlists`); + if (playlists.length === 0) { container.innerHTML = '

No playlists found

'; return; @@ -327,9 +358,26 @@ const playlistsView = {
`; + debugLog('Playlists rendered successfully'); + } catch (error) { debugError('Error loading Spotify playlists:', error); - container.innerHTML = '

Unable to load Spotify playlists

'; + + // Show specific error message + let errorMsg = 'Unable to load Spotify playlists'; + if (error.message) { + if (error.message.includes('Not authenticated') || error.message.includes('session expired')) { + errorMsg = 'Spotify session expired. Please sign in again.'; + } else if (error.message.includes('network') || error.message.includes('fetch')) { + errorMsg = 'Network error. Please check your connection.'; + } else if (error.message.includes('401')) { + errorMsg = 'Spotify session expired. Please sign in again.'; + spotifyAuth.logout(); + } + } + + container.innerHTML = `

${errorMsg}

`; + errorHandler.show(errorMsg); } }, From 30dbaadad6e48c9b9cef00635ff917c1bc04dcc0 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Tue, 31 Mar 2026 16:19:06 +0000 Subject: [PATCH 08/11] Auto-navigate to playlists view after successful Spotify authentication Agent-Logs-Url: https://github.com/Dcode9/MPLayer/sessions/c4383a86-79a9-419c-ac02-37c7f9a8f441 Co-authored-by: Dcode9 <194372792+Dcode9@users.noreply.github.com> --- home/js/spotify-auth.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/home/js/spotify-auth.js b/home/js/spotify-auth.js index 266b1ad..f57d729 100644 --- a/home/js/spotify-auth.js +++ b/home/js/spotify-auth.js @@ -154,11 +154,16 @@ const spotifyAuth = { ui.updateSpotifyButton(); } - // Auto-load playlists if we're on the playlists view - if (typeof router !== 'undefined' && router.currentView === 'playlists') { - if (typeof playlistsView !== 'undefined' && playlistsView.loadSpotifyPlaylists) { - playlistsView.loadSpotifyPlaylists(); - } + // Auto-navigate to playlists view and load playlists + debugLog('Spotify: Auto-navigating to playlists view after authentication'); + if (typeof router !== 'undefined' && router.go) { + // Small delay to ensure DOM is ready + setTimeout(() => { + router.go('playlists'); + }, 100); + } else if (typeof playlistsView !== 'undefined' && playlistsView.loadSpotifyPlaylists) { + // Fallback: just load playlists if router is not available + playlistsView.loadSpotifyPlaylists(); } } catch (error) { From f9044bb5fb520d02412348ce6d48cfcf98af257a Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:54:03 +0000 Subject: [PATCH 09/11] Enhance Spotify playlist debugging with comprehensive logging and troubleshooting guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add detailed debug logging to track API responses and data normalization - Log every step of playlist loading, rendering, and conversion - Add debug output for track-by-track matching progress - Improve error messages with stack traces for better diagnostics - Create comprehensive SPOTIFY_TROUBLESHOOTING.md guide - Add fallback for track count extraction (playlist.track_count) - Include visual indicators (✓/✗) for match success/failure in logs This will help users and developers diagnose why playlists appear incomplete or don't work properly. Agent-Logs-Url: https://github.com/Dcode9/MPLayer/sessions/10d0aed5-e3c3-4ad0-ab8e-945f1d70ad57 Co-authored-by: Dcode9 <194372792+Dcode9@users.noreply.github.com> --- SPOTIFY_TROUBLESHOOTING.md | 321 +++++++++++++++++++++++++++++++++++++ home/js/spotify-api.js | 31 +++- home/js/views.js | 27 +++- 3 files changed, 372 insertions(+), 7 deletions(-) create mode 100644 SPOTIFY_TROUBLESHOOTING.md diff --git a/SPOTIFY_TROUBLESHOOTING.md b/SPOTIFY_TROUBLESHOOTING.md new file mode 100644 index 0000000..1a7f30b --- /dev/null +++ b/SPOTIFY_TROUBLESHOOTING.md @@ -0,0 +1,321 @@ +# Spotify Playlist Troubleshooting Guide + +If you're experiencing issues with Spotify playlists not loading or working properly, follow this guide to diagnose and fix the problem. + +## Common Issues and Solutions + +### Issue 1: "Only the name comes" - Playlists show but no track count or images + +**Symptoms:** +- Playlist cards display only the playlist name +- Track count shows "0 tracks" +- No playlist images/thumbnails +- Or playlists appear blank/incomplete + +**Possible Causes & Solutions:** + +#### Solution A: Check Browser Console for Debug Logs + +1. Open your browser's Developer Tools: + - **Chrome/Edge**: Press `F12` or `Ctrl+Shift+I` (Windows) / `Cmd+Option+I` (Mac) + - **Firefox**: Press `F12` or `Ctrl+Shift+K` (Windows) / `Cmd+Option+K` (Mac) + - **Safari**: Enable Developer menu first, then press `Cmd+Option+I` + +2. Click on the **Console** tab + +3. Look for debug messages starting with `[JioSaavn Player]` including: + - `Spotify API response for playlists:` - Shows raw API data + - `First playlist raw data:` - Shows first playlist structure + - `Normalizing playlist:` - Shows data extraction process + - `Rendering playlist:` - Shows what's being rendered + +4. Check if you see any of these error messages: + - `Not authenticated with Spotify` - You need to sign in + - `Spotify session expired` - Re-authenticate with Spotify + - `Network error` - Check your internet connection + - API errors (401, 403, 429, etc.) - See specific error solutions below + +#### Solution B: Verify Spotify Authentication + +1. Make sure you're signed in to Spotify in the app +2. Check that the "Disconnect Spotify" button is visible (if you see "Connect Spotify", you're not authenticated) +3. If unsure, sign out and sign in again: + - Click "Disconnect Spotify" + - Clear browser localStorage (DevTools → Application → Local Storage → Clear) + - Click "Connect Spotify" and authorize again + +#### Solution C: Check API Response Structure + +In the console, look for the message `First playlist raw data:` and check the structure: + +**Expected structure:** +```javascript +{ + id: "playlist_id_here", + name: "Playlist Name", + images: [{url: "https://..."}], + tracks: { + href: "https://api.spotify.com/v1/playlists/.../tracks", + total: 25 // ← This is the track count + }, + owner: { + display_name: "Owner Name" + } +} +``` + +**If `tracks.total` is missing or 0:** +- The Spotify API might not be returning full data +- Try refreshing the playlists (click the refresh button) +- Check if the playlist is empty on Spotify itself + +#### Solution D: Clear Cached Data + +Sometimes cached data can be stale or corrupted: + +1. Open DevTools → Console +2. Run this command to clear Spotify cache: + ```javascript + localStorage.removeItem('spotify_playlists'); + localStorage.removeItem('spotify_user'); + ``` +3. Refresh the page +4. Navigate to Playlists view again + +### Issue 2: Playlists don't play when clicked + +**Symptoms:** +- Clicking a playlist does nothing +- Loading overlay appears but nothing happens +- Error message appears + +**Diagnostic Steps:** + +#### Step 1: Check Console for Click Events + +When you click a playlist, you should see: +``` +[JioSaavn Player] Opening Spotify playlist: +[JioSaavn Player] Loading overlay shown +[JioSaavn Player] Starting playlist conversion... +[JioSaavn Player] Converting Spotify playlist to JioSaavn +[JioSaavn Player] Retrieved X tracks from Spotify playlist +[JioSaavn Player] Processing track 1/X: - +``` + +**If you don't see these messages:** +- The onclick handler might not be firing +- JavaScript error might be preventing execution +- Check for any red error messages in the console + +**If you see "Error converting Spotify playlist":** +- Check the error details in the console +- Common issues: + - Network timeout + - JioSaavn API unavailable + - Rate limiting + +#### Step 2: Verify Track Matching + +For each track, you should see either: +- `✓ Matched: ` - Track found on JioSaavn +- `✗ No match found for: ` - Track not available on JioSaavn + +**If all tracks show "No match found":** +- JioSaavn API might be down +- Try a playlist with popular Bollywood/Indian music (better match rate) +- Check network connectivity + +#### Step 3: Check Final Result + +At the end, you should see: +``` +[JioSaavn Player] Conversion complete: X/Y tracks matched (Z%) +[JioSaavn Player] Playing X matched tracks +``` + +**If match rate is 0%:** +- JioSaavn doesn't have the songs from your Spotify playlist +- Try a different playlist with more mainstream/Indian music +- Check that JioSaavn API is working (try searching for songs manually) + +### Issue 3: "Spotify session expired" error + +**Solution:** +1. Click "Disconnect Spotify" +2. Wait a few seconds +3. Click "Connect Spotify" +4. Authorize again on Spotify +5. You'll be redirected back and playlists should load + +### Issue 4: "Network error" or "Cannot connect to music service" + +**Solutions:** +1. **Check internet connection** + - Make sure you're online + - Try loading other websites + +2. **Check Spotify API status** + - Visit https://developer.spotify.com/ + - Check if Spotify API is operational + +3. **Check CORS/Network issues** + - Some networks block Spotify API + - Try from a different network/location + - Disable VPN if active (or try with VPN if without doesn't work) + +4. **Browser extensions** + - Ad blockers might block API requests + - Try disabling extensions temporarily + +### Issue 5: Rate limiting (429 error) + +**Symptoms:** +- Error message about too many requests +- Some playlists load but others fail + +**Solution:** +1. Wait 30-60 seconds before trying again +2. The app has built-in retry logic, so just wait +3. Avoid rapidly refreshing or clicking multiple playlists + +### Issue 6: Redirect URI mismatch + +**Symptoms:** +- After clicking "Connect Spotify", you see an error on Spotify's page +- Error message: "INVALID_CLIENT: Invalid redirect URI" + +**Solution:** +See [SPOTIFY_REDIRECT_URI_SETUP.md](./SPOTIFY_REDIRECT_URI_SETUP.md) for detailed instructions. + +Quick fix: +1. Go to [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) +2. Open your app settings +3. Add these exact URIs to "Redirect URIs": + - `https://play.dverse.fun/home/index.html` (for production) + - `http://localhost:8000/home/index.html` (for local development) +4. Click "Save" + +## Advanced Debugging + +### Enable Detailed Logging + +The app already has debug mode enabled. To see all logs: + +1. Open Console (F12) +2. Filter for `[JioSaavn Player]` to see app-specific logs +3. Look for these categories: + - Authentication: `Spotify:` prefix + - API calls: `Spotify API response` + - Playlists: `Loading Spotify playlists`, `Fetched X playlists` + - Rendering: `Rendering playlist:`, `Playlists rendered successfully` + - Conversion: `Converting Spotify playlist`, `Processing track` + - Errors: `[JioSaavn Player Error]` prefix + +### Check Network Tab + +1. Open DevTools → Network tab +2. Click "Playlists" or refresh the view +3. Look for requests to: + - `https://api.spotify.com/v1/me/playlists` - Should return 200 + - `https://api.spotify.com/v1/playlists//tracks` - Should return 200 +4. Click on each request to see: + - Response status (should be 200) + - Response data (should contain playlist/track info) + - Any error messages + +### Inspect Local Storage + +1. Open DevTools → Application tab (Chrome) or Storage tab (Firefox) +2. Navigate to Local Storage → your domain +3. Check these keys: + - `spotify_access_token` - Should have a long string (if authenticated) + - `spotify_token_expiry` - Should be a timestamp in the future + - `spotify_playlists` - Should contain JSON array of playlists + - `spotify_user` - Should contain user info + +**To manually check token expiry:** +```javascript +const expiry = parseInt(localStorage.getItem('spotify_token_expiry')); +const now = Date.now(); +console.log('Token expires in:', Math.round((expiry - now) / 1000 / 60), 'minutes'); +``` + +### Test API Endpoints Manually + +In the console, you can test API calls: + +```javascript +// Check if authenticated +console.log('Authenticated:', spotifyAuth.isAuthenticated); +console.log('Has token:', !!spotifyAuth.getAccessToken()); + +// Test getting playlists +spotifyAPI.getAllPlaylists().then(playlists => { + console.log('Playlists:', playlists); +}); + +// Test getting playlist tracks +spotifyAPI.getPlaylistTracks('YOUR_PLAYLIST_ID').then(tracks => { + console.log('Tracks:', tracks); +}); +``` + +## Still Having Issues? + +If none of the above solutions work: + +1. **Collect Debug Information:** + - Open Console (F12) + - Reproduce the issue + - Copy all console logs + - Take screenshots of the error + +2. **Check Browser Compatibility:** + - Recommended: Chrome, Firefox, Safari (latest versions) + - Clear browser cache and cookies + - Try in incognito/private mode + +3. **Verify Setup:** + - Confirm Spotify Client ID is configured correctly + - Confirm Redirect URIs match exactly + - Check that you're using HTTPS (not HTTP) in production + +4. **Report the Issue:** + - Include browser version and OS + - Include console logs + - Describe exact steps to reproduce + - Mention what you tried from this guide + +## Understanding How It Works + +The Spotify playlist feature works in several stages: + +1. **Authentication**: You sign in with Spotify using OAuth 2.0 +2. **Fetch Playlists**: App calls `/me/playlists` to get your playlist list +3. **Display**: Playlists are shown with name, image, track count, owner +4. **On Click**: When you click a playlist: + - App fetches all tracks from that playlist + - For each track, it searches JioSaavn for a match + - Matched tracks are added to the queue + - Playback starts + +**Important Notes:** +- Not all Spotify songs are on JioSaavn +- Matching is done by searching "song name + artist name" +- Match rates vary by playlist content (Indian music = higher match rate) +- The conversion process takes time (about 100ms per track) + +## Performance Tips + +- **Large playlists (50+ tracks)** will take longer to convert +- **Match rates** are typically 40-70% for international music, 70-90% for Indian music +- **Avoid clicking multiple playlists** rapidly (can cause rate limiting) +- **Use the refresh button** sparingly (max once per minute) + +## Privacy & Security + +- Your Spotify credentials are NEVER stored by this app +- Only the OAuth access token is stored in your browser's localStorage +- The app only requests READ-ONLY access to playlists +- No data is sent to any server except Spotify's official API and JioSaavn diff --git a/home/js/spotify-api.js b/home/js/spotify-api.js index 7b9f375..590a8e2 100644 --- a/home/js/spotify-api.js +++ b/home/js/spotify-api.js @@ -78,6 +78,9 @@ const spotifyAPI = { while (url) { const data = await spotifyAPI.fetchWithAuth(url); + debugLog('Spotify API response for playlists:', data); + debugLog('First playlist raw data:', data.items?.[0]); + const normalized = data.items.map(spotifyAPI.normalizePlaylist); playlists.push(...normalized); @@ -86,6 +89,7 @@ const spotifyAPI = { } debugLog(`Spotify: Fetched ${playlists.length} playlists`); + debugLog('First normalized playlist:', playlists[0]); return playlists; } catch (error) { errorHandler.handleApiError(error, 'Spotify getAllPlaylists'); @@ -164,12 +168,23 @@ const spotifyAPI = { // Normalize playlist object normalizePlaylist: (playlist) => { if (!playlist) return null; + + // Debug log to see the actual structure + if (DEBUG) { + debugLog('Normalizing playlist:', playlist.name); + debugLog('Tracks object:', playlist.tracks); + debugLog('Track count from tracks.total:', playlist.tracks?.total); + } + + // Spotify API returns tracks as an object with 'href' and 'total' properties + const trackCount = playlist.tracks?.total || playlist.track_count || 0; + return { id: playlist.id, name: playlist.name, description: playlist.description || '', img: playlist.images?.[0]?.url || 'https://placehold.co/300/333/fff?text=Playlist', - trackCount: playlist.tracks?.total || 0, + trackCount: trackCount, owner: playlist.owner?.display_name || 'Unknown', isPublic: playlist.public, isCollaborative: playlist.collaborative, @@ -222,12 +237,16 @@ const spotifyAPI = { // Convert Spotify playlist to playable JioSaavn tracks convertPlaylistToJioSaavn: async (spotifyPlaylistId, onProgress) => { try { + debugLog(`Converting Spotify playlist ${spotifyPlaylistId} to JioSaavn`); const spotifyTracks = await spotifyAPI.getPlaylistTracks(spotifyPlaylistId); + debugLog(`Retrieved ${spotifyTracks.length} tracks from Spotify playlist`); + const jiosaavnTracks = []; let matched = 0; for (let i = 0; i < spotifyTracks.length; i++) { const spotifyTrack = spotifyTracks[i]; + debugLog(`Processing track ${i + 1}/${spotifyTracks.length}: ${spotifyTrack.name} - ${spotifyTrack.artist}`); // Report progress if (onProgress) { @@ -242,21 +261,27 @@ const spotifyAPI = { const jiosaavnTrack = await spotifyAPI.findJioSaavnMatch(spotifyTrack); if (jiosaavnTrack) { + debugLog(`✓ Matched: ${jiosaavnTrack.name}`); jiosaavnTracks.push(jiosaavnTrack); matched++; + } else { + debugLog(`✗ No match found for: ${spotifyTrack.name}`); } // Add small delay to avoid overwhelming the API await new Promise(r => setTimeout(r, 100)); } - debugLog(`Spotify: Converted ${matched}/${spotifyTracks.length} tracks to JioSaavn`); - return { + const result = { tracks: jiosaavnTracks, total: spotifyTracks.length, matched: matched, }; + + debugLog(`Conversion complete: ${matched}/${spotifyTracks.length} tracks matched (${Math.round(matched/spotifyTracks.length*100)}%)`); + return result; } catch (error) { + debugError('Error in convertPlaylistToJioSaavn:', error); errorHandler.handleApiError(error, 'Spotify convertPlaylistToJioSaavn'); return { tracks: [], total: 0, matched: 0 }; } diff --git a/home/js/views.js b/home/js/views.js index 5301fbb..86eb778 100644 --- a/home/js/views.js +++ b/home/js/views.js @@ -331,6 +331,7 @@ const playlistsView = { localStorage.setItem('spotify_playlists', JSON.stringify(playlists)); debugLog(`Fetched ${playlists.length} playlists`); + debugLog('Sample playlist data:', playlists[0]); if (playlists.length === 0) { container.innerHTML = '

No playlists found

'; @@ -340,7 +341,9 @@ const playlistsView = { // Render playlists container.innerHTML = `
- ${playlists.map(playlist => ` + ${playlists.map(playlist => { + debugLog(`Rendering playlist: ${playlist.name}, tracks: ${playlist.trackCount}, img: ${playlist.img}`); + return `
@@ -354,7 +357,7 @@ const playlistsView = {

${playlist.trackCount} tracks • ${searchManager.escapeHtml(playlist.owner)}

Spotify
- `).join('')} + `}).join('')}
`; @@ -362,6 +365,7 @@ const playlistsView = { } catch (error) { debugError('Error loading Spotify playlists:', error); + debugError('Error stack:', error.stack); // Show specific error message let errorMsg = 'Unable to load Spotify playlists'; @@ -382,8 +386,13 @@ const playlistsView = { }, openSpotifyPlaylist: async (playlistId) => { + debugLog(`Opening Spotify playlist: ${playlistId}`); + const container = document.getElementById('spotify-playlists-container'); - if (!container) return; + if (!container) { + debugError('Container not found when trying to open playlist'); + return; + } // Show loading overlay const overlay = document.createElement('div'); @@ -402,8 +411,10 @@ const playlistsView = {
`; document.body.appendChild(overlay); + debugLog('Loading overlay shown'); try { + debugLog('Starting playlist conversion...'); // Convert Spotify playlist to JioSaavn tracks const result = await spotifyAPI.convertPlaylistToJioSaavn(playlistId, (progress) => { const progressBar = document.getElementById('convert-progress'); @@ -413,26 +424,34 @@ const playlistsView = { const percentage = Math.round((progress.current / progress.total) * 100); progressBar.style.width = `${percentage}%`; statusText.textContent = `Processing ${progress.current} of ${progress.total} tracks (${progress.matched} matched)`; + debugLog(`Conversion progress: ${percentage}%, ${progress.matched}/${progress.total} matched`); } }); + debugLog('Conversion complete:', result); + // Remove overlay overlay.remove(); if (result.tracks.length === 0) { + debugError('No tracks matched from Spotify playlist'); errorHandler.show('Could not find any matching tracks on JioSaavn', 4000); return; } + debugLog(`Playing ${result.tracks.length} matched tracks`); // Play the converted tracks player.setQueue(result.tracks, 0); // Show success message const matchRate = Math.round((result.matched / result.total) * 100); - errorHandler.show(`Successfully converted ${result.matched} of ${result.total} tracks (${matchRate}%)`, 5000); + const successMsg = `Successfully converted ${result.matched} of ${result.total} tracks (${matchRate}%)`; + debugLog(successMsg); + errorHandler.show(successMsg, 5000); } catch (error) { debugError('Error converting Spotify playlist:', error); + debugError('Error details:', error.message, error.stack); overlay.remove(); errorHandler.show('Failed to convert playlist. Please try again.'); } From 9c3e155806f651ea8bb0760b9d8188339e9129f8 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:19:45 +0000 Subject: [PATCH 10/11] Add comprehensive console messages documentation - Create CONSOLE_MESSAGES.md explaining all common console warnings - Document that Tailwind CDN warning is expected (no build system) - Explain browser extension errors (webcomponents, overlay_bundle) - Clarify performance warnings (forced reflow) are informational - Add references to console docs in SPOTIFY_SETUP.md and SPOTIFY_TROUBLESHOOTING.md The console errors reported by the user are mostly: 1. Expected warnings (Tailwind CDN) 2. Browser extension conflicts (not our code) 3. Performance hints (not actual errors) None prevent the app from working. This documentation helps users understand what's normal vs. what requires action. Agent-Logs-Url: https://github.com/Dcode9/MPLayer/sessions/df496fd5-235a-4c2c-a5be-9d2abfec817a Co-authored-by: Dcode9 <194372792+Dcode9@users.noreply.github.com> --- CONSOLE_MESSAGES.md | 192 +++++++++++++++++++++++++++++++++++++ SPOTIFY_SETUP.md | 2 + SPOTIFY_TROUBLESHOOTING.md | 2 + 3 files changed, 196 insertions(+) create mode 100644 CONSOLE_MESSAGES.md diff --git a/CONSOLE_MESSAGES.md b/CONSOLE_MESSAGES.md new file mode 100644 index 0000000..264c542 --- /dev/null +++ b/CONSOLE_MESSAGES.md @@ -0,0 +1,192 @@ +# Console Messages Explanation + +This document explains the console messages you might see when using MPLayer (D'Tunes). + +## Normal/Expected Messages + +### 1. Tailwind CDN Warning + +``` +cdn.tailwindcss.com should not be used in production. To use Tailwind CSS in production, install it as a PostCSS plugin or use the Tailwind CLI +``` + +**What it means:** Tailwind CSS recommends using their build tools for production sites. + +**Why we use it:** MPLayer is designed to work without a build system. It's a static site that can be served from any web server without compilation or build steps. The Tailwind CDN is the simplest way to use Tailwind in this architecture. + +**Is it a problem?** No, this is just a recommendation. The CDN works fine for this use case. The warning is informational only. + +**Should you worry?** No. This is expected and by design. + +--- + +### 2. Image Lazy Loading Message + +``` +[Intervention] Images loaded lazily and replaced with placeholders. Load events are deferred. +``` + +**What it means:** The browser is optimizing image loading for better performance. + +**Why it happens:** Modern browsers automatically lazy-load images to improve page load speed. + +**Is it a problem?** No, this is a browser optimization feature. + +**Should you worry?** No. This improves performance. + +--- + +### 3. Forced Reflow Warnings + +``` +[Violation] Forced reflow while executing JavaScript took XXms +``` + +**What it means:** JavaScript is causing the browser to recalculate page layout multiple times. + +**Why it happens:** When JavaScript reads layout properties (like element size/position) and immediately modifies the DOM, the browser has to recalculate layout. + +**Is it a problem?** Only if it happens frequently and causes visible lag. The times shown (42-71ms) are generally acceptable. + +**Should you worry?** No, unless you notice performance issues. These are just performance hints, not errors. + +--- + +## Browser Extension Messages (Not Our Code) + +### 4. Custom Element Already Defined Error + +``` +Uncaught Error: A custom element with name 'mce-autosize-textarea' has already been defined. + at webcomponents-ce.js:33:363 + at overlay_bundle.js:149:5562 +``` + +**What it means:** A browser extension (likely Microsoft Edge's built-in features) is trying to define a custom HTML element twice. + +**Why it happens:** Browser extensions can inject code into web pages. Sometimes they conflict with each other or with the page. + +**Is it from our code?** NO. This is from a browser extension. Notice the file names: +- `webcomponents-ce.js` - Not one of our files +- `overlay_bundle.js` - Not one of our files + +**Should you worry?** No. This doesn't affect MPLayer's functionality. + +**How to confirm:** Disable browser extensions and reload. The error will disappear. + +--- + +### 5. 404 Error on "undefined:1" + +``` +undefined:1 Failed to load resource: the server responded with a status of 404 () +``` + +**What it means:** Something is trying to load a resource that doesn't exist. + +**Why it happens:** This often comes from: +- Browser extensions +- Analytics/tracking scripts blocked by ad blockers +- Temporary network issues +- OAuth redirects (Spotify authentication) + +**Is it from our code?** Unlikely. The URL shows "undefined:1" which suggests external code. + +**Should you worry?** No, unless core functionality is broken (music doesn't play, playlists don't load, etc.). + +--- + +## How to Get a Clean Console + +If you want to reduce console noise: + +### Option 1: Filter Console Messages + +In Chrome/Edge DevTools: +1. Open Console (F12) +2. Use the filter dropdown +3. Select "Errors" only to hide warnings +4. Or use the search box to filter specific messages + +### Option 2: Disable Verbose Warnings + +In the Console tab: +1. Click the settings gear icon +2. Uncheck "Violations" under "Console settings" + +### Option 3: Test in Incognito/Private Mode + +1. Open an incognito/private window +2. Disable extensions in incognito mode +3. Reload MPLayer +4. Most extension-related errors will disappear + +--- + +## Real Errors to Watch For + +These would indicate actual problems: + +### Authentication Errors +``` +[JioSaavn Player Error] Spotify token exchange error +[JioSaavn Player Error] Not authenticated with Spotify +``` +**Action:** Re-authenticate with Spotify + +### API Errors +``` +[JioSaavn Player Error] API Error: 401/403/429 +[JioSaavn Player Error] Cannot connect to music service +``` +**Action:** Check internet connection, wait if rate-limited + +### Player Errors +``` +[JioSaavn Player Error] Failed to load track +[JioSaavn Player Error] Audio playback error +``` +**Action:** Try a different track, check network connection + +--- + +## Debug Mode + +MPLayer runs in debug mode by default (see `home/js/config.js`, line 28: `const DEBUG = true`). + +This means you'll see many informational messages prefixed with `[JioSaavn Player]`: +- `Spotify: Authentication successful` +- `Loading Spotify playlists...` +- `Fetched X playlists` +- `Processing track X/Y` +- `✓ Matched: Song Name` + +**These are helpful for troubleshooting but are not errors.** + +To disable debug logging, you would need to edit `home/js/config.js` and set `DEBUG = false`, but this is **not recommended** as it makes troubleshooting harder. + +--- + +## Summary + +**Most console messages you see are:** +1. Informational warnings (Tailwind CDN, lazy loading) +2. Browser extension conflicts (webcomponents, overlay scripts) +3. Performance hints (forced reflow) +4. Debug information from MPLayer + +**None of these prevent the app from working.** + +**Only worry if:** +- Music doesn't play +- Playlists don't load +- Spotify authentication fails +- You see actual JavaScript errors (not warnings) from our code files + +--- + +## Related Documentation + +- [Spotify Setup Guide](./SPOTIFY_SETUP.md) +- [Spotify Troubleshooting](./SPOTIFY_TROUBLESHOOTING.md) +- [Spotify Redirect URI Setup](./SPOTIFY_REDIRECT_URI_SETUP.md) diff --git a/SPOTIFY_SETUP.md b/SPOTIFY_SETUP.md index 4607b37..0e7e67a 100644 --- a/SPOTIFY_SETUP.md +++ b/SPOTIFY_SETUP.md @@ -2,6 +2,8 @@ This guide will help you set up Spotify integration in MPLayer (D'Tunes) to import and play your Spotify playlists. +> **Note:** If you see console warnings or messages in your browser's developer tools, please refer to [CONSOLE_MESSAGES.md](./CONSOLE_MESSAGES.md) for explanations. Most console messages are informational and don't indicate problems. + ## Features - **Sign in with Spotify**: Securely authenticate using OAuth 2.0 with PKCE (Proof Key for Code Exchange) diff --git a/SPOTIFY_TROUBLESHOOTING.md b/SPOTIFY_TROUBLESHOOTING.md index 1a7f30b..703f328 100644 --- a/SPOTIFY_TROUBLESHOOTING.md +++ b/SPOTIFY_TROUBLESHOOTING.md @@ -2,6 +2,8 @@ If you're experiencing issues with Spotify playlists not loading or working properly, follow this guide to diagnose and fix the problem. +> **Note:** Many console messages are informational warnings, not errors. See [CONSOLE_MESSAGES.md](./CONSOLE_MESSAGES.md) for explanations of common console messages like Tailwind CDN warnings, browser extension errors, and performance hints. + ## Common Issues and Solutions ### Issue 1: "Only the name comes" - Playlists show but no track count or images From 379e53454f43957cb149641befb53ad8681c4413 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:14:09 +0000 Subject: [PATCH 11/11] Fix Spotify playlist fetching with improved error handling and debugging - Change getAllPlaylists to throw errors instead of silently returning empty array - Add detailed logging at each step of playlist fetching process - Add validation for API response structure - Improve error messages in loadSpotifyPlaylists with specific error handling - Handle authentication, permission, and API errors separately - Show user-friendly error messages for each failure scenario This helps diagnose why playlists fail to load after successful authentication. Now errors will be visible in console and shown to the user. Agent-Logs-Url: https://github.com/Dcode9/MPLayer/sessions/d3046f8d-2e2a-4b2a-9530-d111bc0b3068 Co-authored-by: Dcode9 <194372792+Dcode9@users.noreply.github.com> --- home/js/spotify-api.js | 26 +++++++++++++++++++++++--- home/js/views.js | 23 ++++++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/home/js/spotify-api.js b/home/js/spotify-api.js index 590a8e2..9dda2a4 100644 --- a/home/js/spotify-api.js +++ b/home/js/spotify-api.js @@ -76,9 +76,19 @@ const spotifyAPI = { const playlists = []; let url = '/me/playlists?limit=50'; + debugLog('Starting to fetch playlists from Spotify...'); + while (url) { + debugLog('Fetching playlist page:', url); const data = await spotifyAPI.fetchWithAuth(url); + + if (!data || !data.items) { + debugError('Invalid response from Spotify playlists API:', data); + throw new Error('Invalid response from Spotify playlists API'); + } + debugLog('Spotify API response for playlists:', data); + debugLog('Number of playlists in this page:', data.items.length); debugLog('First playlist raw data:', data.items?.[0]); const normalized = data.items.map(spotifyAPI.normalizePlaylist); @@ -86,14 +96,24 @@ const spotifyAPI = { // Check for next page url = data.next; + if (url) { + debugLog('More playlists to fetch, continuing...'); + } } - debugLog(`Spotify: Fetched ${playlists.length} playlists`); - debugLog('First normalized playlist:', playlists[0]); + debugLog(`Spotify: Successfully fetched ${playlists.length} playlists`); + if (playlists.length > 0) { + debugLog('First normalized playlist:', playlists[0]); + } else { + debugLog('No playlists found in Spotify account'); + } return playlists; } catch (error) { + debugError('Error in getAllPlaylists:', error); + debugError('Error message:', error.message); + debugError('Error stack:', error.stack); errorHandler.handleApiError(error, 'Spotify getAllPlaylists'); - return []; + throw error; // Re-throw to let caller handle it } }, diff --git a/home/js/views.js b/home/js/views.js index 86eb778..2486324 100644 --- a/home/js/views.js +++ b/home/js/views.js @@ -326,7 +326,28 @@ const playlistsView = { debugLog('Fetching playlists...'); // Fetch playlists - const playlists = await spotifyAPI.getAllPlaylists(); + let playlists; + try { + playlists = await spotifyAPI.getAllPlaylists(); + } catch (playlistError) { + debugError('Failed to fetch playlists:', playlistError); + // Show specific error to user + let errorMsg = 'Failed to load Spotify playlists'; + if (playlistError.message.includes('Not authenticated')) { + errorMsg = 'Spotify authentication failed. Please try signing in again.'; + } else if (playlistError.message.includes('401')) { + errorMsg = 'Spotify session expired. Please sign in again.'; + spotifyAuth.logout(); + } else if (playlistError.message.includes('403')) { + errorMsg = 'Access denied. Please check your Spotify app permissions.'; + } else if (playlistError.message) { + errorMsg = `Error: ${playlistError.message}`; + } + container.innerHTML = `

${errorMsg}

`; + errorHandler.show(errorMsg); + return; + } + state.spotifyPlaylists = playlists; localStorage.setItem('spotify_playlists', JSON.stringify(playlists));