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 @@