function parseUrl(url) { var params = {}; var parser = document.createElement('a'); parser.href = url; var query = parser.search.substring(1); var vars = query.split('&'); for (var i = 0; i < vars.length; i++) { var pair = vars[i].split('='); if (pair[0] != "country" && pair[0] != "language" && pair[0] != "loadmoresongs" && pair[0] != "youtubeurl" && pair[0] != "spotifyurl" && pair[0] != "applemusicurl") { params[pair[0]] = decodeURIComponent(pair[1]); } } return params; } function capitalizeWords(str) { // Capitalize first word of playlist and remove + return str.replace(/\b\w/g, function (l) { return l.toUpperCase() }).replace(/\+/g, ' '); } function removeByFromSong(song) { // alert(song.replace(/\s+by\s+/gi, ' ')) //replace " by " with " - ". Use dash to create playlist description when extracting the artists in the php return song.replace(/\s+[bB][yY]\s+/, ' - '); } //used to extract song from localStorage for playlist and importspotify pages function extractSongTitleFromUrl(encodedUrl) { try { // Decode the URL first var decodedUrl = decodeURIComponent(encodedUrl); var urlParams = parseUrl(decodedUrl); // If songtitle exists, decode it var songTitle = urlParams.songtitle ? urlParams.songtitle : ''; // Decode the song title before removing "by" or "By" songTitle = decodeURIComponent(songTitle); // Remove "by" or "By" from the song title songTitle = removeByFromSong(songTitle); console.log('Song Title: ', songTitle); return songTitle; } catch (e) { console.error("Error parsing song title: ", e); return ''; } } function setToLocalStorage() { // Clear previous songs from localStorage localStorage.removeItem('playlistTitle'); localStorage.removeItem('songs'); var pageurl = document.URL; var params = parseUrl(pageurl); var playlistTitle = ""; // If the URL contains '/importspotify' or '/playlist' if (pageurl.includes('moodplaylist.com/importspotify') || pageurl.includes('moodplaylist.com/playlist')) { // Get the song list from the HTML var songList = document.getElementById('gptans').getElementsByTagName('li'); // Convert HTMLCollection to Array var songs = Array.prototype.slice.call(songList); // Extract the artist names and join them var artists = songs.map(function (li) { var songAndArtist = li.innerText.split(/\s+by\s+/gi); if (songAndArtist.length > 1) return songAndArtist[1]; // return the artist name if present }); // Convert the artists array to a Set to remove duplicates, then convert it back to an array. // [...new Set(artists)], the spread operator is used to convert a Set back to an array. new Set(artists) creates a Set from the array artists, which removes any duplicate elements because sets can only contain unique elements. artists = [...new Set(artists)]; // Reduce the artist list to fit in 99 characters var playlistArtists = artists.reduce(function (acc, artist) { var newAcc = acc ? (acc + ', ' + artist) : artist; // Check the length with " | MOODPlaylist.com" and if it exceeds 99, return the accumulator if ((capitalizeWords(newAcc) + ' | MOODPlaylist.com').length > 99) return acc; return newAcc; }, ''); // Form the playlistTitle playlistTitle = capitalizeWords(playlistArtists); //if you are on the playlist page then songs in localStorage are mapped from Wishlist localStorage if (pageurl.includes('moodplaylist.com/playlist')) { // Retrieve songs from wishlist localStorage and set to to songs local storage var wishlist = JSON.parse(localStorage.getItem('wishlist')); console.log('Wishlist: ', wishlist); // Extract song titles from wishlist URLs var songs = wishlist.map(function (item) { // console.log('Current item: ', item); var songTitle = extractSongTitleFromUrl(item.url); return songTitle; }); // console.log('Songs in storage: ', songs); // Save songs to localStorage localStorage.setItem('songs', JSON.stringify(songs)); console.log("songs in storage 000"+ localStorage.getItem("songs")) } else if (pageurl.includes('moodplaylist.com/importspotify')) { // Retrieve songs from wishlist localStorage and set to to songs local storage var wishlist = JSON.parse(localStorage.getItem('importSpotifyPlaylist')); console.log('importSpotifyPlaylist: ', wishlist); // Extract song titles from wishlist URLs var songs = wishlist.map(function (item) { // console.log('Current item: ', item); var songTitle = extractSongTitleFromUrl(item.url); return songTitle; }); // console.log('Songs in storage: ', songs); // Save songs to localStorage localStorage.setItem('songs', JSON.stringify(songs)); } } else { // If not a special case URL //We then check if appending ' | MOODPlaylist.com' to newTitleFromParams would cause the string to exceed 99 characters. If so, we immediately break the loop, //preventing further parameters from being added to the title. This is because we want to keep the total length of the playlist title within the limit of 99 characters. var titleFromParams = ""; for (var key in params) { var newTitleFromParams = titleFromParams ? (titleFromParams + " | " + capitalizeWords(params[key])) : capitalizeWords(params[key]); // Check the length with " | MOODPlaylist.com" and if it exceeds 99, return the accumulator if ((newTitleFromParams + ' | MOODPlaylist.com').length > 99) break; titleFromParams = newTitleFromParams; } playlistTitle = titleFromParams; //If your are on the homepage, extract songs from gptans // Get the song list from the HTML var songList = document.getElementById('gptans').getElementsByTagName('li'); // Convert HTMLCollection to Array var songs = Array.prototype.slice.call(songList); // Map the Array of LIs to an Array of song strings songs = songs.map(function (li) { return removeByFromSong(li.innerText); }); // Save songs to localStorage localStorage.setItem('songs', JSON.stringify(songs)); console.log("MY SONGS"+localStorage.getItem('songs')); } playlistTitle += ' | MOODPlaylist.com'; // Append " | MOODPlaylist.com" at the end of the playlist title // Save playlist title to localStorage localStorage.setItem('playlistTitle', playlistTitle); // If there is no playlistTitleInput element then generate one based on the previous url var playlistTitleInput = document.getElementById("playlistTitleInput"); //if playlistTitleInput doesn't exist like in the case that you are NOT on the spotifyplaylistedit.html page(bc it only exists on the spotifyplaylistedit.html page) then set the URL generated playlistTitle to local storage if (!playlistTitleInput) { // Save playlist title to localStorage localStorage.setItem('playlistTitle', playlistTitle); } //else if playlist exists (you are on the spotifyplaylistedit.html page) and its not empty use the playlistTitleInput value to create the playlist title, we use the input value bc user can change the playlistTitle to what ever they want else if (playlistTitleInput && playlistTitleInput.value != "") { localStorage.setItem('playlistTitle', playlistTitleInput.value); } //else if the user fails to input anything and leaves blank then - this is form validation. else if (playlistTitleInput && playlistTitleInput.value == "") { localStorage.setItem('playlistTitle', document.getElementById("playlistTitleInputHidden").value); } console.log("MY SONGS"+localStorage.getItem('songs')); } //Listen for ecit-spotify button to be clicked document.body.addEventListener("click", function (event) { if (event.target.id === "edit-spotify") { setToLocalStorage(); //window.location = "https://moodplaylist.com/spotifyplaylistedit" window.open("https://moodplaylist.com/spotifyplaylistedit", "_blank"); } //Listen for export-spotify button to be clicked if (event.target.id === "generate-spotify-top" || event.target.id === "generate-spotify-bottom") { setToLocalStorage(); //Commented out 19/10/23 - and replaced with generateSpotify0(); i.e the Premiumm Spotify Export function. generateSpotify(); function code will remain as a reminder. generateSpotify(); is the function that allows users to export to Moodplaylist spotify account. //generateSpotify(); generateSpotify0(); } //Commented out 19/10/23 - this was the logic to enable the secret Premium Spotify Export button //Secret Premium User Button on spotifyplaylistedit.html for premium users //this will generate a spotify playlist on a users OWN spotify account, but the user needs to be authenticated at the back end (on the spotify dash) //if (event.target.id === "secretspotify") { // setToLocalStorage(); //generateSpotify0(); //} }); ///Original function function generateSpotify() { //This old code is needed for when initially you create a new user (the deverloper), spotifyplaylist will create a // user in the DB and then a refresh token for the deverloper, that refresh token is then used by spotifyplaylistsuccess > ajax > createPlaylistNoLogin.php // so playlist can be created using the the developers refresh token, the refresh token means that new access tokens can be generated and thus new playlists, on the deverlopers account /* var clientId = '683a7cf785d34b76bec487258baaf945'; var redirectUri = encodeURIComponent('https://moodplaylist.com/spotifyplaylist'); var scope = encodeURIComponent('playlist-modify-public playlist-modify-private'); window.location = `https://accounts.spotify.com/authorize?response_type=code&client_id=${clientId}&scope=${scope}&redirect_uri=${redirectUri}`; if (newWindow) { newWindow.focus(); } else { // Alert the user if the new window couldn't be opened, which may be due to pop-up blockers alert("Please allow pop-ups for this site"); } */ //this is the new code which doesn't require users to login to spotify, just load the page and spotifyplaylistsuccess > ajax > createPlaylistNoLogin.php loads and uses the deverlopers //refresh token to generate a playlist window.location = "https://moodplaylist.com/spotifyplaylistsuccess"; } // Helper functions function generateRandomString(length) { let text = ''; let possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < length; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; } async function generateCodeChallenge(codeVerifier) { function base64encode(arrayBuffer) { return btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer))) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); } const encoder = new TextEncoder(); const data = encoder.encode(codeVerifier); const digest = await window.crypto.subtle.digest('SHA-256', data); return base64encode(digest); } // new authorize function that is used for premium "Export to Spotify Playlist" (export to users OWN spotify account) //NOTE NAME CHANGED TO generateSpotify0() //When generating the authorization URL for buying credits, we append the URL parameter state //This way, when the user gets redirected back to spotify_callback.php, you can check this parameter to determine their intention. //Allowing you differentiate between users who are redirected to spotify_callback.php to buy credits and users who are redirected to create a playlist. //If the intent parameter isn't provided when the function is called, then by default, let's set its value to null." /** * Function to generate Spotify's OAuth 2.0 authorization URL. * * @param {string} [intent=null] - The user's intent (e.g., "buy_credits"). * If not provided, defaults to null. * * @returns {void} Redirects to the generated Spotify authorization URL. * * @description * The purpose of the intent parameter is to differentiate between user actions: * - Buying credits * - Creating a playlist * * Spotify's OAuth 2.0 process uses a 'state' parameter primarily for security (preventing CSRF attacks). * To embed custom data (like 'intent') in the OAuth flow without introducing new parameters, * we encode it within the 'state' parameter. This ensures Spotify preserves our custom data across redirects. */ async function generateSpotify0(intent = null) { var clientId = '871eb8f042c342e8b6174ceec5775862'; var redirectUri = 'https://moodplaylist.com/spotify_callback.php'; var scope = 'playlist-modify-public playlist-modify-private'; let codeVerifier = generateRandomString(128); let codeChallenge = await generateCodeChallenge(codeVerifier); let stateData = { randomState: generateRandomString(16), intent: intent }; let state = btoa(JSON.stringify(stateData)); localStorage.setItem('code_verifier', codeVerifier); let args = new URLSearchParams({ response_type: 'code', client_id: clientId, scope: scope, redirect_uri: redirectUri, state: state, code_challenge_method: 'S256', code_challenge: codeChallenge }); window.location = 'https://accounts.spotify.com/authorize?' + args; }