From e12bd9998f8a0d7d3e078f7f6ca8e563c14697bb Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 31 May 2023 13:50:33 +0200 Subject: [PATCH 1/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ff9d8e..428b3f7 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ To set up the project, follow these steps: 1. Clone the repository: - ```` + ``` git clone https://github.com/w3bdesign/spotify-import.git cd spotify-playlist-creator ``` From 0f90e403f446bf9cda5745367121a43a51184148 Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 31 May 2023 15:52:26 +0200 Subject: [PATCH 2/6] Song recommendations --- .env.example | 4 +- app.py | 81 +++++++++- static/styles.css | 347 ++++++++++++++++++++++++++----------------- templates/index.html | 108 +++++++++++++- 4 files changed, 399 insertions(+), 141 deletions(-) diff --git a/.env.example b/.env.example index 30b1701..7d40b8d 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,6 @@ SPOTIPY_CLIENT_ID=your_client_id_here SPOTIPY_CLIENT_SECRET=your_client_secret_here SPOTIPY_REDIRECT_URI=http://localhost:5000/callback -SPOTIPY_USER_NAME=your_user_name_here \ No newline at end of file +SPOTIPY_USER_NAME=your_user_name_here +OPENAI_API_KEY=your_openai_api_key_here +OPENAI_API_BASE_URL=your_openai_api_base_url \ No newline at end of file diff --git a/app.py b/app.py index fe3b430..381e1df 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,8 @@ from flask import Flask, render_template, request, redirect, url_for import spotipy +import openai from flask import request, redirect, url_for, session +from flask import Flask, request, jsonify from dotenv import load_dotenv from spotipy.oauth2 import SpotifyOAuth import os @@ -14,6 +16,13 @@ client_secret = os.getenv("SPOTIPY_CLIENT_SECRET") redirect_uri = os.getenv("SPOTIPY_REDIRECT_URI") username = os.getenv("SPOTIPY_USER_NAME") +openai_api_key = os.getenv("OPENAI_API_KEY") +openai_api_base_url = os.getenv("OPENAI_API_BASE_URL") + +# Set the OpenAI API key and base URL +openai.api_key = openai_api_key +openai.api_base = openai_api_base_url + # Create a Spotify OAuth object sp_oauth = SpotifyOAuth( @@ -24,7 +33,54 @@ username=username, ) - +""" +Generate song suggestions based on a seed song. + +:param seed_song: The seed song to generate suggestions from. +:type seed_song: str +:param num_suggestions: The number of song suggestions to generate, defaults to 10. +:type num_suggestions: int +:return: A list of song suggestions based on the seed song. +:rtype: List[str] +""" +def generate_song_suggestions(seed_song, num_suggestions=10): + prompt = f"Based on the song '{seed_song}', please suggest {num_suggestions} similar songs." + + response = openai.ChatCompletion.create( + model='gpt-4', + messages=[ + {'role': 'user', 'content': prompt}, + ] +) + + print("response: ", response) + + return response + + #suggestions = response.choices[0].text.strip().split("\n") + #return suggestions[:num_suggestions] + + +""" +Defines a POST endpoint that generates song suggestions based on a given seed song. + +Returns: + A JSON object containing a list of suggested songs based on the seed song. +""" +@app.route('/generate_suggestions', methods=['POST']) +def generate_suggestions(): + data = request.get_json() + seed_song = data.get('song') + suggestions = generate_song_suggestions(seed_song) + return jsonify(suggestions) + +""" +This function is the callback endpoint for the application's OAuth2 authentication flow. +It receives an authorization code from the user's grant, exchanges it for an access token, and stores the token in the user's session. +If successful, the user is redirected to the application's index page. +If the user does not provide a code or the token cannot be obtained, an error message is returned. +If successful, redirects the user to the application's index page. If unsuccessful, returns an error message string. +""" @app.route("/callback") def callback(): code = request.args.get("code") @@ -36,8 +92,26 @@ def callback(): return "Error: No code provided or token not obtained." -@app.route("/", methods=["GET", "POST"]) + +@app.route("/", methods=["GET"]) def index(): + return render_template( + "index.html", + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + ) + + +""" +Defines the index route for the web application. Handles GET and POST requests. +On a POST request, creates a Spotify playlist based on user input and adds recommended songs. +Returns a redirect to the success page on a successful POST request. +On a GET request, renders the index.html template with the necessary Spotify credentials. +:return: A redirect to the success page on a successful POST request. Otherwise, renders the index.html template. +""" +@app.route("/generate_playlist", methods=["POST"]) +def create_playlist(): if request.method == "POST": playlist_name = request.form["playlist_name"] playlist_description = request.form["playlist_description"] @@ -76,6 +150,9 @@ def index(): redirect_uri=redirect_uri, ) +""" +Renders the success.html template with the playlist_name parameter if it exists in the current request arguments. +""" @app.route("/success") def success(): playlist_name = request.args.get("playlist_name", "") diff --git a/static/styles.css b/static/styles.css index 6906780..4e9e16c 100644 --- a/static/styles.css +++ b/static/styles.css @@ -1,143 +1,220 @@ body { - font-family: "Arial", sans-serif; - background-color: #181818; - padding: 20px; - color: #fff; - display: flex; - justify-content: center; - align-items: center; - min-height: 80vh; - flex-direction: column; - } - - h1 { - font-size: 2.5rem; - font-weight: bold; - margin-bottom: 30px; - text-align: center; - } - - .form { - background-color: #282828; - padding: 35px; - border-radius: 5px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); - max-width: 600px; - min-width: 500px; - margin: 0 auto; - } - - .form__label { - display: block; - margin-bottom: 10px; - font-weight: bold; - font-size: 1.1rem; - margin-top: 10px; - } - - .form__input, - .form__textarea { - width: 100%; - padding: 7px; - border: 1px solid #3c3c3c; - border-radius: 3px; - font-size: 14px; - background-color: #3c3c3c; - color: #fff; - margin-bottom: 15px; - } - - .form__textarea { - resize: vertical; - } + font-family: "Arial", sans-serif; + background-color: #181818; + padding: 20px; + color: #fff; + display: flex; + justify-content: center; + align-items: center; + min-height: 80vh; + flex-direction: column; +} - #song_recommendations { - height: 300px; +h1 { + font-size: 2rem; + font-weight: bold; + margin-bottom: 15px; + text-align: center; } - - .form__submit { - background-color: #1db954; - color: #fff; - border: none; - padding: 10px 20px; - border-radius: 50px; - font-size: 14px; - cursor: pointer; - margin-top: 10px; - transition: background-color 0.3s; - } - - .form__submit:hover { - background-color: #1ed760; - } - - .form__submit:active { - background-color: #1aa34d; - } - - .form__input:focus, - .form__textarea:focus { - outline-color: #1db954; - } - - .message { - font-size: 1.1rem; - max-width: 600px; - margin: 0 auto 20px auto; - padding: 10px; - text-align: center; - border-radius: 5px; - } - - .message--success { - background-color: #1db954; - color: #fff; - padding:15px; - margin-bottom: 30px; - } - - .message--error { - background-color: #e74c3c; - color: #fff; - } - - .spotify-connection-message { - font-size: 1.1rem; - max-width: 600px; - margin: 0 auto 30px auto; - padding: 10px; - text-align: center; - border-radius: 5px; - } - - .spotify-connection-message--success { - background-color: #1db954; - color: #fff; + +h2 { + font-size: 1.5rem; + font-weight: bold; + margin-bottom: 15px; + text-align: center; +} + +.form { + background-color: #282828; + padding: 35px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + max-width: 600px; + min-width: 500px; + margin: 0 auto; + margin-top: 20px; +} + +.form__label { + display: block; + margin-bottom: 10px; + font-weight: bold; + font-size: 1.1rem; + margin-top: 10px; +} + +.form__input, +.form__textarea { + width: 100%; + padding: 7px; + border: 1px solid #3c3c3c; + border-radius: 3px; + font-size: 14px; + background-color: #3c3c3c; + color: #fff; + margin-bottom: 15px; +} + +.form__textarea { + resize: vertical; +} + +#song_recommendations { + height: 300px; +} + +.song-suggestion { + font-weight: bold; + font-size: 1.2em; + margin-bottom: 0.5em; +} + +.form__submit { + background-color: #1db954; + color: #fff; + border: none; + padding: 10px 20px; + border-radius: 50px; + font-size: 14px; + cursor: pointer; + margin-top: 10px; + transition: background-color 0.3s; +} + +.form__submit:hover { + background-color: #1ed760; +} + +.form__submit:active { + background-color: #1aa34d; +} + +.form__input:focus, +.form__textarea:focus { + outline-color: #1db954; +} + +.message { + font-size: 1.1rem; + max-width: 600px; + margin: 0 auto 20px auto; + padding: 10px; + text-align: center; + border-radius: 5px; +} + +.message--success { + background-color: #1db954; + color: #fff; + padding: 15px; + margin-bottom: 30px; +} + +.message--error { + background-color: #e74c3c; + color: #fff; +} + +.spotify-connection-message { + font-size: 1.1rem; + max-width: 600px; + margin: 0 auto 30px auto; + padding: 10px; + text-align: center; + border-radius: 5px; +} + +.spotify-connection-message--success { + background-color: #1db954; + color: #fff; +} + +.spotify-connection-message--error { + background-color: #e74c3c; + color: #fff; +} + +.button { + display: inline-block; + padding: 20px; + background-color: #1d4ed8; + color: white; + text-decoration: none; + border-radius: 5px; + margin-top: 20px; + transition: background-color 0.4s ease; +} + +.button:hover { + background-color: #3b82f6; +} + +.submit-wrapper { + display: flex; + justify-content: center; + width: 100%; + justify-items: center; +} + +.loading-spinner { + width: 48px; + height: 48px; + display: inline-block; + animation: spin 1.2s linear infinite; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); } - - .spotify-connection-message--error { - background-color: #e74c3c; - color: #fff; + 100% { + transform: rotate(360deg); } +} + +.loading-spinner path { + fill: #1db954; +} + +.loading-spinner path:nth-child(2) { + fill: #095023; +} + +#song-suggestions { + display: flex; + justify-content: center; + align-items: center; + /*min-height: 50px; /* Adjust this value according to your needs */ + opacity: 0; + visibility: hidden; + transition: opacity 0.6s ease-in-out, visibility 0.6s ease-in-out; + height: 0; + +} + +#song-suggestions.show { + opacity: 1; + visibility: visible; + height: 100%; - .button { - display: inline-block; - padding: 20px; - background-color: #1D4ED8; - color: white; - text-decoration: none; - border-radius: 5px; - margin-top: 20px; - transition: background-color 0.4s ease; +} + +@keyframes fadeIn { + 0% { + opacity: 0; } - - .button:hover { - background-color: #3B82F6; + 100% { + opacity: 1; } - - .submit-wrapper { - display: flex; - justify-content: center; - width: 100%; - justify-items: center; - } \ No newline at end of file +} + +.form-background { + background-color: #282828; + padding: 35px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + max-width: 600px; + min-width: 500px; + margin: 0 auto; + margin-top: 20px; +} diff --git a/templates/index.html b/templates/index.html index 8ca715e..5f94ed1 100644 --- a/templates/index.html +++ b/templates/index.html @@ -9,7 +9,8 @@ Create Spotify Playlist -

Create Spotify Playlist

+ + {% if client_id and client_secret and redirect_uri %}

Spotify developer data has been loaded from the .env file. @@ -21,7 +22,20 @@

Create Spotify Playlist

{% endif %} -
+

Generate song recommendations

+ + + +
+ +
+
+
+
+

Create Spotify Playlist

+
Create Spotify Playlist name="song_recommendations" rows="10" cols="50" - required >
+ + From 742ed27deebbf1c0500db2554a12b5210be8d6c6 Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 31 May 2023 16:30:24 +0200 Subject: [PATCH 3/6] Play button styling --- app.py | 54 ++++++++++++++++++++------ static/styles.css | 57 +++++++++++++++++++++++---- templates/index.html | 91 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 178 insertions(+), 24 deletions(-) diff --git a/app.py b/app.py index 381e1df..944dadd 100644 --- a/app.py +++ b/app.py @@ -43,22 +43,50 @@ :return: A list of song suggestions based on the seed song. :rtype: List[str] """ + + def generate_song_suggestions(seed_song, num_suggestions=10): prompt = f"Based on the song '{seed_song}', please suggest {num_suggestions} similar songs." response = openai.ChatCompletion.create( - model='gpt-4', - messages=[ - {'role': 'user', 'content': prompt}, - ] -) - - print("response: ", response) + model="gpt-4", + messages=[ + {"role": "user", "content": prompt}, + ], + ) return response - #suggestions = response.choices[0].text.strip().split("\n") - #return suggestions[:num_suggestions] + +""" +Route that searches for a song given the song name passed as a query parameter. +If the song name is not provided, returns a JSON error message with status code 400. +If the song is found, returns the preview URL of the first matching track as a JSON response. +If the song is not found, returns a JSON error message with status code 404. +:return: A JSON response with the preview URL of the song or an error message. +""" +@app.route("/search_song", methods=["GET"]) +def search_song(): + song_name = request.args.get("song_name") + sp = spotipy.Spotify( + auth_manager=SpotifyOAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope="playlist-modify-public", + username=username, + ) + ) + if not song_name: + return jsonify({"error": "No song name provided"}), 400 + + results = sp.search(q=song_name, type="track", limit=1) + if results and results["tracks"]["items"]: + track = results["tracks"]["items"][0] + song_url = track["preview_url"] + return jsonify({"song_url": song_url}) + else: + return jsonify({"error": "Song not found"}), 404 """ @@ -67,13 +95,14 @@ def generate_song_suggestions(seed_song, num_suggestions=10): Returns: A JSON object containing a list of suggested songs based on the seed song. """ -@app.route('/generate_suggestions', methods=['POST']) +@app.route("/generate_suggestions", methods=["POST"]) def generate_suggestions(): data = request.get_json() - seed_song = data.get('song') + seed_song = data.get("song") suggestions = generate_song_suggestions(seed_song) return jsonify(suggestions) + """ This function is the callback endpoint for the application's OAuth2 authentication flow. It receives an authorization code from the user's grant, exchanges it for an access token, and stores the token in the user's session. @@ -92,7 +121,6 @@ def callback(): return "Error: No code provided or token not obtained." - @app.route("/", methods=["GET"]) def index(): return render_template( @@ -150,6 +178,7 @@ def create_playlist(): redirect_uri=redirect_uri, ) + """ Renders the success.html template with the playlist_name parameter if it exists in the current request arguments. """ @@ -158,5 +187,6 @@ def success(): playlist_name = request.args.get("playlist_name", "") return render_template("success.html", playlist_name=playlist_name) + if __name__ == "__main__": app.run(debug=True) diff --git a/static/styles.css b/static/styles.css index 4e9e16c..240b421 100644 --- a/static/styles.css +++ b/static/styles.css @@ -13,14 +13,14 @@ body { h1 { font-size: 2rem; font-weight: bold; - margin-bottom: 15px; + margin-bottom: 10px; text-align: center; } h2 { font-size: 1.5rem; font-weight: bold; - margin-bottom: 15px; + margin-bottom: 10px; text-align: center; } @@ -28,9 +28,8 @@ h2 { background-color: #282828; padding: 35px; border-radius: 5px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); - max-width: 600px; - min-width: 500px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + min-width: 610px; margin: 0 auto; margin-top: 20px; } @@ -213,8 +212,52 @@ h2 { padding: 35px; border-radius: 5px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); - max-width: 600px; - min-width: 500px; + min-width: 610px; margin: 0 auto; margin-top: 20px; } + +.play-button { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border: none; + border-radius: 50%; + background-color: #1db954; + color: #ffffff; + font-size: 12px; + font-weight: 700; + cursor: pointer; + position: relative; + overflow: hidden; + transition: background-color 0.3s; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); + padding: 0; +} + +.play-button:hover { + background-color: #1ed760; +} + +.play-button::before { + content: ""; + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.1); + top: 0; + left: -100%; + transition: left 0.3s; +} + +.play-button:hover::before { + left: 100%; +} + +.play-button svg { + width: 12px; + height: 12px; + fill: #ffffff; +} diff --git a/templates/index.html b/templates/index.html index 5f94ed1..3522b19 100644 --- a/templates/index.html +++ b/templates/index.html @@ -9,8 +9,6 @@ Create Spotify Playlist - - {% if client_id and client_secret and redirect_uri %}

Spotify developer data has been loaded from the .env file. @@ -143,19 +141,102 @@

Create Spotify Playlist

} else { // Display the recommendations const list = document.createElement("ul"); - for (const suggestion of suggestions) { + for (const songName of suggestions) { const listItem = document.createElement("li"); - listItem.textContent = suggestion; listItem.classList.add("song-suggestion"); + + const songNameSpan = document.createElement("span"); + songNameSpan.textContent = songName; + listItem.appendChild(songNameSpan); + + // Create the Play button + const playButton = document.createElement("button"); + // playButton.textContent = "Play"; + playButton.setAttribute("data-state", "play"); + playButton.innerHTML = ` + + + + `; + playButton.classList.add("play-button"); + + // Add the event listener to handle the Play button click + playButton.addEventListener("click", () => { + playSong(songName, playButton); + }); + + listItem.appendChild(playButton); list.appendChild(listItem); } suggestionsDiv.appendChild(list); } - // Add the .show class to animate the opacity + // Add the .show class to animate the opacity and height suggestionsDiv.classList.add("show"); } + + let currentAudio = null; + + async function fetchSongUrl(songName) { + try { + const response = await fetch( + `/search_song?song_name=${encodeURIComponent(songName)}` + ); + if (response.ok) { + const data = await response.json(); + return data.song_url; + } else { + console.error(`Error fetching song URL: ${response.statusText}`); + return null; + } + } catch (error) { + console.error(`Error fetching song URL: ${error.message}`); + return null; + } + } + + function pauseCurrentAudio() { + if (currentAudio) { + currentAudio.pause(); + if (currentAudio.playButton) { + //currentAudio.playButton.textContent = "Play"; + playButton.setAttribute("data-state", "play"); + playButton.innerHTML = ` + + + + `; + } + } + } + + async function playSong(songName, playButton) { + if (currentAudio && currentAudio.songName === songName) { + pauseCurrentAudio(); + currentAudio.playButton = null; + currentAudio = null; + return; + } + + const songUrl = await fetchSongUrl(songName); + if (!songUrl) { + console.error("Song URL not found"); + return; + } + + pauseCurrentAudio(); + + currentAudio = new Audio(songUrl); + currentAudio.songName = songName; + currentAudio.playButton = playButton; + currentAudio.play(); + //playButton.textContent = "Pause"; + const playIcon = playButton.querySelector(".play-icon"); + const pauseIcon = playButton.querySelector(".pause-icon"); + playIcon.style.display = "none"; + pauseIcon.style.display = "block"; + } From 28198fa74c8810ed64bdfe9476b56fc28d4e12e0 Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 31 May 2023 16:39:54 +0200 Subject: [PATCH 4/6] Fix song suggestion styling --- app.py | 3 ++- static/styles.css | 15 +++++++++++++++ templates/index.html | 39 ++++++++++++++++++++++----------------- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/app.py b/app.py index 944dadd..11028c4 100644 --- a/app.py +++ b/app.py @@ -49,7 +49,8 @@ def generate_song_suggestions(seed_song, num_suggestions=10): prompt = f"Based on the song '{seed_song}', please suggest {num_suggestions} similar songs." response = openai.ChatCompletion.create( - model="gpt-4", + #model="gpt-4", + model="gpt-3.5-turbo", messages=[ {"role": "user", "content": prompt}, ], diff --git a/static/styles.css b/static/styles.css index 240b421..1ff9728 100644 --- a/static/styles.css +++ b/static/styles.css @@ -261,3 +261,18 @@ h2 { height: 12px; fill: #ffffff; } + +.song-suggestions-table { + width: 100%; + border-collapse: collapse; +} + +.song-suggestions-table td { + padding: 8px; + text-align: left; + border-bottom: 1px solid #ddd; +} + +.song-suggestions-table tr:hover { + background-color: #181818; +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 3522b19..17feba7 100644 --- a/templates/index.html +++ b/templates/index.html @@ -139,25 +139,28 @@

Create Spotify Playlist

const spinner = createLoadingSpinner(); suggestionsDiv.appendChild(spinner); } else { - // Display the recommendations - const list = document.createElement("ul"); + // Create a table to display the recommendations + const table = document.createElement("table"); + table.classList.add("song-suggestions-table"); + for (const songName of suggestions) { - const listItem = document.createElement("li"); - listItem.classList.add("song-suggestion"); + const row = document.createElement("tr"); + row.classList.add("song-suggestion"); + + const songNameCell = document.createElement("td"); + songNameCell.textContent = songName; + row.appendChild(songNameCell); - const songNameSpan = document.createElement("span"); - songNameSpan.textContent = songName; - listItem.appendChild(songNameSpan); + // Create the Play button cell + const playButtonCell = document.createElement("td"); - // Create the Play button const playButton = document.createElement("button"); - // playButton.textContent = "Play"; playButton.setAttribute("data-state", "play"); playButton.innerHTML = ` - - - - `; + + + +`; playButton.classList.add("play-button"); // Add the event listener to handle the Play button click @@ -165,11 +168,13 @@

Create Spotify Playlist

playSong(songName, playButton); }); - listItem.appendChild(playButton); - list.appendChild(listItem); + playButtonCell.appendChild(playButton); + row.appendChild(playButtonCell); + + table.appendChild(row); } - suggestionsDiv.appendChild(list); + suggestionsDiv.appendChild(table); } // Add the .show class to animate the opacity and height @@ -231,7 +236,7 @@

Create Spotify Playlist

currentAudio.songName = songName; currentAudio.playButton = playButton; currentAudio.play(); - //playButton.textContent = "Pause"; + const playIcon = playButton.querySelector(".play-icon"); const pauseIcon = playButton.querySelector(".pause-icon"); playIcon.style.display = "none"; From 6c7d7dd235b6d6ab4d9e000375c13253361f7637 Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 31 May 2023 16:46:20 +0200 Subject: [PATCH 5/6] Fix play/pause bug --- templates/index.html | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/templates/index.html b/templates/index.html index 17feba7..704849b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -205,13 +205,12 @@

Create Spotify Playlist

if (currentAudio) { currentAudio.pause(); if (currentAudio.playButton) { - //currentAudio.playButton.textContent = "Play"; - playButton.setAttribute("data-state", "play"); - playButton.innerHTML = ` - - - - `; + currentAudio.playButton.setAttribute("data-state", "play"); + currentAudio.playButton.innerHTML = ` + + + +`; } } } From 0375150e6a3384c24b94eed355fd01999e80c666 Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 31 May 2023 16:54:35 +0200 Subject: [PATCH 6/6] Refactor --- static/{ => css}/styles.css | 0 static/scripts/main.js | 177 +++++++++++++++++++++++++++++++++++ templates/index.html | 178 +----------------------------------- 3 files changed, 179 insertions(+), 176 deletions(-) rename static/{ => css}/styles.css (100%) create mode 100644 static/scripts/main.js diff --git a/static/styles.css b/static/css/styles.css similarity index 100% rename from static/styles.css rename to static/css/styles.css diff --git a/static/scripts/main.js b/static/scripts/main.js new file mode 100644 index 0000000..3a9ef32 --- /dev/null +++ b/static/scripts/main.js @@ -0,0 +1,177 @@ +document.addEventListener("DOMContentLoaded", function () { + document + .getElementById("song-form") + .addEventListener("submit", async (event) => { + event.preventDefault(); + const songInput = document.getElementById("song_name"); + const song = songInput.value; + const suggestions = await generateSongSuggestions(song); + displaySuggestions(suggestions); + }); + + // Event listener for the second form + document + .getElementById("playlist-form") + .addEventListener("submit", async (event) => { + event.preventDefault(); + // Handle the second form submission here + }); + + async function generateSongSuggestions(song) { + displaySuggestions(); // Show the loading spinner + + const response = await fetch("/generate_suggestions", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ song }), + }); + + if (response.ok) { + const data = await response.json(); + const messageContent = data.choices[0].message.content; + const suggestions = extractSongSuggestions(messageContent); + displaySuggestions(suggestions); // Hide the loading spinner and display the suggestions + return suggestions; + } else { + throw new Error("Failed to fetch suggestions."); + } + } + + function createLoadingSpinner() { + const spinner = document.createElement("div"); + spinner.classList.add("loading-spinner"); + spinner.innerHTML = ` + + + + `; + return spinner; + } + + function extractSongSuggestions(messageContent) { + const lines = messageContent.split("\n"); + const suggestions = lines + .filter((line) => line.match(/^\d+\./)) + .map((line) => + line + .substring(line.indexOf(".") + 1) + .trim() + .replace(/\*\*/g, "") + ); + return suggestions; + } + + function displaySuggestions(suggestions) { + const suggestionsDiv = document.getElementById("song-suggestions"); + suggestionsDiv.innerHTML = ""; + + if (!suggestions) { + // Show the loading spinner + const spinner = createLoadingSpinner(); + suggestionsDiv.appendChild(spinner); + } else { + // Create a table to display the recommendations + const table = document.createElement("table"); + table.classList.add("song-suggestions-table"); + + for (const songName of suggestions) { + const row = document.createElement("tr"); + row.classList.add("song-suggestion"); + + const songNameCell = document.createElement("td"); + songNameCell.textContent = songName; + row.appendChild(songNameCell); + + // Create the Play button cell + const playButtonCell = document.createElement("td"); + + const playButton = document.createElement("button"); + playButton.setAttribute("data-state", "play"); + playButton.innerHTML = ` + + + +`; + playButton.classList.add("play-button"); + + // Add the event listener to handle the Play button click + playButton.addEventListener("click", () => { + playSong(songName, playButton); + }); + + playButtonCell.appendChild(playButton); + row.appendChild(playButtonCell); + + table.appendChild(row); + } + + suggestionsDiv.appendChild(table); + } + + // Add the .show class to animate the opacity and height + suggestionsDiv.classList.add("show"); + } + + let currentAudio = null; + + async function fetchSongUrl(songName) { + try { + const response = await fetch( + `/search_song?song_name=${encodeURIComponent(songName)}` + ); + if (response.ok) { + const data = await response.json(); + return data.song_url; + } else { + console.error(`Error fetching song URL: ${response.statusText}`); + return null; + } + } catch (error) { + console.error(`Error fetching song URL: ${error.message}`); + return null; + } + } + + function pauseCurrentAudio() { + if (currentAudio) { + currentAudio.pause(); + if (currentAudio.playButton) { + currentAudio.playButton.setAttribute("data-state", "play"); + currentAudio.playButton.innerHTML = ` + + + +`; + } + } + } + + async function playSong(songName, playButton) { + if (currentAudio && currentAudio.songName === songName) { + pauseCurrentAudio(); + currentAudio.playButton = null; + currentAudio = null; + return; + } + + const songUrl = await fetchSongUrl(songName); + if (!songUrl) { + console.error("Song URL not found"); + return; + } + + pauseCurrentAudio(); + + currentAudio = new Audio(songUrl); + currentAudio.songName = songName; + currentAudio.playButton = playButton; + currentAudio.play(); + + const playIcon = playButton.querySelector(".play-icon"); + const pauseIcon = playButton.querySelector(".pause-icon"); + playIcon.style.display = "none"; + pauseIcon.style.display = "block"; + } + + // Paste your entire JavaScript code here +}); diff --git a/templates/index.html b/templates/index.html index 704849b..f958223 100644 --- a/templates/index.html +++ b/templates/index.html @@ -4,7 +4,7 @@ Create Spotify Playlist @@ -67,180 +67,6 @@

Create Spotify Playlist

- +