Skip to content

Commit

Permalink
Merge pull request #6 from JOwen-ster/connect-front-to-back
Browse files Browse the repository at this point in the history
Connect frontend to backend + Redesign
  • Loading branch information
JOwen-ster authored Dec 11, 2024
2 parents b7c367a + 52d9325 commit cfbf9a0
Show file tree
Hide file tree
Showing 25 changed files with 928 additions and 272 deletions.
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
clientID="🩷"
clientSecret="💜"
GOOGLE_API_KEY="💛"

SPOTIFY_CLIENT_ID="💚"
SPOTIFY_CLIENT_SECRET="❤️"
SPOTIFY_CLIENT_SECRET="🧡"
86 changes: 73 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,84 @@ The app is designed to make the playlist conversion process as straightforward a

---

## 🖼️ Screenshots

*(TBD)*

---

## How to Run the Project

*(TBD)*
1. **Set Up Environment Variables**
- Obtain Spotify Developer credentials (Client ID and Client Secret) from the [Spotify Developer Portal](https://developer.spotify.com/dashboard/).
- Generate a Google API Key from the [Google Cloud Console](https://console.cloud.google.com/).
- Obtain YouTube credentials (Client ID and Client Secret) from the [Google Cloud Console](https://console.cloud.google.com/).
- Create a `.env` file in the root directory using the format in `env.example`:
```env
clientID="🩷"
clientSecret="💜"
GOOGLE_API_KEY="💛"
SPOTIFY_CLIENT_ID="💚"
SPOTIFY_CLIENT_SECRET="🧡"
```
2. **Install Dependencies**
- Ensure Python v3 is installed on your system.
- Open a terminal, navigate to the `backend/api` folder by running:
```bash
cd backend/api
```
- Install the required Python dependencies by running:
```bash
pip install -r requirements.txt
```
3. **Run the Application**
Open **two terminals**:
- **Terminal 1:**
- From the root directory of the project, navigate to the `backend/api` folder:
```bash
cd backend/api
```
- Start the Flask server:
- On Mac:
```bash
python3 flask-server.py
```
- On Windows:
```bash
python flask-server.py
```
- **Terminal 2:**
- From the root directory of the project, navigate to the `frontend` folder:
```bash
cd frontend
```
- Install the required Node.js dependencies by running:
```bash
npm install
```
- Start the Svelte development server:
```bash
npm run dev
```
4. **Use the Application**
- Open a browser and navigate to the provided URL (usually `http://localhost:3000`).
- Click **Login to Spotify** to authenticate.
- Select a playlist and click **Submit Playlist**.
- Click **Login to YouTube**, and you'll be redirected to a success page.
- Check your YouTube account for the exported playlist.
**Note:** Both terminals must remain running simultaneously for the application to work.
---
## Key Features
---
- **Easy Playlist Transfer:** Move your music effortlessly between Spotify and YouTube with minimal input.
- **Accurate Song Matching:** Leverages song titles and artist names to find the closest matches on YouTube.
- **Dynamic Playlist Creation:** Automatically generates and saves a YouTube playlist with the selected songs.
- **Responsive Design:** Enjoy a user-friendly interface on both desktop and mobile devices.
## 🖼️ Screenshots
<img width="1470" alt="Screenshot 2024-12-10 at 7 51 38 PM" src="https://github.com/user-attachments/assets/dd5f18dc-ade0-4e4d-9a6e-060a789d6a4c">
<img width="1470" alt="Screenshot 2024-12-10 at 7 52 18 PM" src="https://github.com/user-attachments/assets/dcd8c846-91aa-4ed8-ad16-cd4d83150e5c">
<img width="1470" alt="Screenshot 2024-12-10 at 7 52 38 PM" src="https://github.com/user-attachments/assets/c3323423-31c2-46c1-b89a-cbb19880e3d7">
<img width="1470" alt="Screenshot 2024-12-10 at 7 53 01 PM" src="https://github.com/user-attachments/assets/d33a7cb6-1ff0-44ad-b619-642883d6963f">
<img width="1470" alt="Screenshot 2024-12-10 at 7 53 55 PM" src="https://github.com/user-attachments/assets/e62cda6f-64c1-457e-a5d2-1b2f5ba07d07">
<img width="974" alt="Screenshot 2024-12-10 at 7 54 37 PM" src="https://github.com/user-attachments/assets/9aeb9053-d2ee-433d-8ef4-dace9b95b20e">
---
Expand Down
10 changes: 4 additions & 6 deletions backend/api/YTMutator.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,22 @@ def createUserPlaylist(self, playlist_name):
playlist_object = self.youtube_account.playlists().insert(part=part, body=resource).execute()
return playlist_object

def addSongToUserPlaylist(self, playlist_object, url):
yt_song = self.getSongObject(url=url)
def addSongToUserPlaylist(self, playlist_object, videoId):
part = 'id,snippet,status,contentDetails'
insert_data = {
'kind': 'youtube#playlistItem',
'snippet': {
'playlistId': playlist_object['id'],
'resourceId': {
'kind': 'youtube#video',
'videoId': yt_song['items'][0]['id']
'videoId': videoId
}
}
}
self.youtube_account.playlistItems().insert(part=part, body=insert_data).execute()

def exportLinks(self, playlist_object: object, links: dict):
iterableLinks = links['youtube_links']
for link in iterableLinks:
def exportLinks(self, playlist_object: object, links: list):
for link in links:
self.addSongToUserPlaylist(playlist_object, link)

def getSongObject(self, url):
Expand Down
Binary file added backend/api/__pycache__/YTMutator.cpython-311.pyc
Binary file not shown.
Binary file modified backend/api/__pycache__/YTMutator.cpython-313.pyc
Binary file not shown.
Binary file added backend/api/__pycache__/scraper.cpython-311.pyc
Binary file not shown.
Binary file modified backend/api/__pycache__/scraper.cpython-313.pyc
Binary file not shown.
Binary file added backend/api/__pycache__/spotify.cpython-311.pyc
Binary file not shown.
Binary file modified backend/api/__pycache__/spotify.cpython-313.pyc
Binary file not shown.
103 changes: 83 additions & 20 deletions backend/api/flask-server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@
from time import sleep

import flask
from flask import jsonify , request

import google.oauth2.credentials
import google_auth_oauthlib.flow
import googleapiclient.discovery

from flask_cors import CORS

from urllib.parse import urlparse, parse_qs

from YTMutator import YouTubeMutator

import scraper
import spotify

from scraper import get_yt_links, GOOGLE_API_KEY

# import scraper
# The CLIENT_SECRETS_FILE variable specifies the name of a file that contains
# the OAuth 2.0 information for this application, including its client_id and
# client_secret.
Expand All @@ -31,7 +38,16 @@

youtube_manager = None

@app.route('/')
global playlistsSpotify
playlistsSpotify = ["happy", "zcool playlist", "bears", "Wizard of oz"]
songsForYoutube = []
youtubeIds = []
playlistName = ""
credentials = None

CORS(app, supports_credentials=True) # Need to add this and CORS import to run flask server along with sveltekit

@app.route('/', methods=['GET'])
def index():
if 'credentials' not in flask.session:
return flask.redirect('authorize')
Expand Down Expand Up @@ -72,6 +88,7 @@ def authorize():

@app.route('/oauth2callback')
def oauth2callback():
global credentials
# Specify the state when creating the flow in the callback so that it can
# verify the authorization server response.
state = flask.session['state']
Expand All @@ -95,42 +112,88 @@ def oauth2callback():
'scopes': credentials.scopes
}

return flask.redirect('/PlaylistExporter')
return flask.redirect("/PlaylistExporter")

@app.route('/PlaylistExporter')
@app.route("/PlaylistExporter")
def playlistExporter():
global songsForYoutube
songsForYoutube = spotify.send_user_playlist(playlist_name=playlistName)
global youtubeIds
youtubeIds = get_yt_links(songsForYoutube, GOOGLE_API_KEY)
global credentials
# call playlist scraper spotify only function

if "credentials" not in flask.session:
return flask.redirect("/authorize") # Redirect user to authenticate

# Load the credentials from the session.
credentials = google.oauth2.credentials.Credentials(
**flask.session['credentials'])
credentials = google.oauth2.credentials.Credentials(**flask.session["credentials"])

youtube_instance = googleapiclient.discovery.build(
API_SERVICE_NAME, API_VERSION, credentials=credentials)
API_SERVICE_NAME, API_VERSION, credentials=credentials
)

global youtube_manager
if not youtube_manager:
youtube_manager = YouTubeMutator(youtube_instance)

created_playlist = youtube_manager.createUserPlaylist(playlist_name='TESTING_API')
youtube_manager.exportLinks(playlist_object=created_playlist, links=scraper.youtubeLinks)
created_playlist = youtube_manager.createUserPlaylist(playlist_name=playlistName)
print("created_playlist: ", created_playlist)
print("youtubeIds: ", youtubeIds)
youtube_manager.exportLinks(created_playlist, youtubeIds) # error

#youtube_manager.addSongToUserPlaylist(
# playlist_object=youtube_manager.getUserPlaylist('TESTING_API'),
# url='https://www.youtube.com/watch?v=-OkrC6h2H5k'
# )

#view = youtube_manager.getUserPlaylist('TESTING_API')
# youtube_manager.updateUserPlaylistInfo(playlist_object=view)

return '<p>Lorem Ipsum</p>'
# Need to return back to website
# return jsonify(response_data)
return flask.redirect("http://localhost:5173/success")


def channels_list_by_username(client, **kwargs):
response = client.channels().list(**kwargs).execute()
return flask.jsonify(**response)

@app.route("/spotifyLogin")
def loginSpotify():
global playlistsSpotify
playlistsSpotify = spotify.login_user()
return flask.redirect("http://localhost:5173/")


@app.route("/getPlaylists", methods=['GET'])
def fetch_spotify_playlist():
try:
# Check if playlistsSpotify is empty or not loaded
if not playlistsSpotify:
raise ValueError("No playlists available. Please log in to Spotify first.")
return jsonify({"playlists": list(playlistsSpotify)})
except Exception as e:
return jsonify({"error": f"Failed to fetch playlists: {str(e)}"}), 500


@app.route("/getSongs", methods=['POST'])
def spotify_playlist():
data = request.get_json()
print("data: ", data)
playlist = data.get('selected')
print("playlist:", playlist)
global playlistName
playlistName = playlist
#global songsForYoutube
#songsForYoutube = spotify.send_user_playlist(playlist_name=playlist)
#global youtubeIds
#youtubeIds = get_yt_links(songsForYoutube, GOOGLE_API_KEY)
# Process the playlist URL (you can add your logic here)
# Example: Extract playlist ID, fetch tracks, etc.
# response = {
# "message": f" Playlist {playlist} received and processed.",
# "playlist": playlist,
# "songs": songsForYoutube,
# }

# Return a response
return flask.redirect("http://localhost:5173/")


if __name__ == '__main__':
# When running locally, disable OAuthlib's HTTPs verification. When
# running in production *do not* leave this option enabled.
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
app.run('localhost', 8090, debug=True)
app.run('localhost', port=8090, debug=True)
8 changes: 8 additions & 0 deletions backend/api/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Flask==3.1.0
Flask_Cors==5.0.0
google_api_python_client==2.154.0
google_auth_oauthlib==1.2.1
protobuf==5.29.1
python-dotenv==1.0.1
Requests==2.32.3
spotipy==2.24.0
12 changes: 5 additions & 7 deletions backend/api/scraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,18 @@ def get_yt_links(search_object, google_api_key):
# Validate response structure
if results.get("pageInfo", {}).get("totalResults", 0) > 0:
video_id = results["items"][0]["id"]["videoId"]
youtube_links.append(f"https://www.youtube.com/watch?v={video_id}")
youtube_links.append(video_id)
else:
not_found.append(search_query)
except requests.exceptions.RequestException as e:
print(f"Error during YouTube search for '{search_query}': {e}")
not_found.append(search_query)

return {
"youtube_links": youtube_links,
}
return youtube_links


spotifySongs = spotify.send_user_playlist("test")
#spotifySongs = spotify.send_user_playlist("test")

youtubeLinks = get_yt_links(spotifySongs, GOOGLE_API_KEY)
#youtubeLinks = get_yt_links(spotifySongs, GOOGLE_API_KEY)

print(youtubeLinks)
#print(youtubeLinks)
Loading

0 comments on commit cfbf9a0

Please sign in to comment.