Skip to content

Commit

Permalink
working
Browse files Browse the repository at this point in the history
  • Loading branch information
very-doge-wow committed Dec 31, 2023
1 parent 0621c39 commit 4572368
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 47 deletions.
67 changes: 31 additions & 36 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,20 @@
import base64
import threading
import time

from flask import abort, Flask, redirect, render_template, request, session, url_for, make_response
from util import get_paginated_track_list, generate_random_string, query_artist_spotify, create_spotify_playlist
import json
import logging
import os
import requests

import settings
from util import get_paginated_track_list, generate_random_string, query_artist_spotify, create_spotify_playlist, add_tracks_to_spotify_playlist
from settings import *

logging.basicConfig(
format='%(asctime)s - %(levelname)s - %(message)s', level=logging.DEBUG
)

# Client info for Spotify
SPOTIFY_CLIENT_ID = os.environ['SPOTIFY_CLIENT_ID']
SPOTIFY_CLIENT_SECRET = os.environ['SPOTIFY_CLIENT_SECRET']

# Client info for Amazon Music
AMAZON_TOKEN = os.environ['AMAZON_TOKEN']
AMAZON_X_API_KEY = os.environ['AMAZON_X_API_KEY']

# Spotify API endpoints and configs
SPOTIFY_BASE_ENDPOINT = 'https://api.spotify.com/v1'
SPOTIFY_TOKEN_URL = 'https://accounts.spotify.com/api/token'
SPOTIFY_SEARCH_ENDPOINT = f'{SPOTIFY_BASE_ENDPOINT}/search'
SPOTIFY_ME_ENDPOINT = f'{SPOTIFY_BASE_ENDPOINT}/me'
SPOTIFY_REDIRECT_URL = 'http://127.0.0.1:5000/callback'
SPOTIFY_STATE_KEY = 'spotify_auth_state'

# Amazon API endpoints
AMAZON_BASE_ENDPOINT = 'https://api.music.amazon.dev/v1'
AMAZON_TOKEN_URL = 'https://api.amazon.com/auth/o2/token'
AMAZON_ME_ENDPOINT = f'{AMAZON_BASE_ENDPOINT}/me/'

# Startup
app = Flask(__name__)
app.secret_key = os.environ['SECRET_KEY']
Expand All @@ -41,9 +23,10 @@
@app.route('/auth/spotify')
def auth_spotify():
state = generate_random_string(16)
scope = '%20'.join(SPOTIFY_API_SCOPES)
response = make_response(redirect(
'https://accounts.spotify.com/authorize?' +
f'response_type=code&client_id={SPOTIFY_CLIENT_ID}&scope=user-read-private%20user-read-email&redirect_uri={SPOTIFY_REDIRECT_URL}&state={state}'
f'{SPOTIFY_AUTH_URL}?' +
f'response_type=code&client_id={SPOTIFY_CLIENT_ID}&scope={scope}&redirect_uri={SPOTIFY_REDIRECT_URL}&state={state}'
))
response.set_cookie(SPOTIFY_STATE_KEY, state)
return response
Expand All @@ -62,7 +45,7 @@ def callback():
response.delete_cookie(SPOTIFY_STATE_KEY)

auth_options = {
'url': 'https://accounts.spotify.com/api/token',
'url': SPOTIFY_TOKEN_URL,
'data': {
'code': code,
'redirect_uri': SPOTIFY_REDIRECT_URL,
Expand Down Expand Up @@ -94,7 +77,7 @@ def search_spotify():
if request.method == 'POST':
artist = request.form.get('artist')
if artist:
res = query_artist_spotify(spotify_search_endpoint=SPOTIFY_SEARCH_ENDPOINT, artist=artist)
res = query_artist_spotify(artist=artist)
res_data = res.json()

if res_data.get('error') or res.status_code != 200:
Expand All @@ -117,7 +100,6 @@ def search_spotify():
@app.route('/amazon/playlists', methods=['GET'])
def playlists_amazon():
"""Simple example search for playlists."""

# first get user_id
if not session.get('amazon_user'):
headers = {
Expand Down Expand Up @@ -158,18 +140,31 @@ def playlists_amazon():

@app.route('/amazon/migrate', methods=['POST'])
def migrate_playlist():
playlist_id = request.form.get('submitValue')
session['progress'] = 0
original_playlist_id = request.form.get('submitValue')
settings.PROGRESS = 0

# get the playlist from amazon API (including tracks)
original_playlist = get_paginated_track_list(amazon_base_endpoint=AMAZON_BASE_ENDPOINT, amazon_token=AMAZON_TOKEN, amazon_x_api_key=AMAZON_X_API_KEY, playlist={}, playlist_id=playlist_id, cursor=None)
original_playlist = get_paginated_track_list(playlist={}, playlist_id=original_playlist_id, cursor=None)

# we only need artist and name of the tracks
tracks = []
for edge in original_playlist['tracks']['edges']:
track = {
'artist': edge['node']['artists'][0]['name'],
'title': edge['node']['title'],
}
tracks.append(track)

# create new playlist at spotify if not exists
create_spotify_playlist(spotify_me_endpoint=SPOTIFY_ME_ENDPOINT, name=original_playlist['title'])
new_playlist_id = create_spotify_playlist(name=original_playlist['title'])

# ToDo: Make this call asynch to template returns!
thread = threading.Thread(target=add_tracks_to_spotify_playlist, name="migration", args=[session['spotify_access_token'], new_playlist_id, tracks])
thread.start()

return render_template(template_name_or_list='migrate.html', content=original_playlist)


@app.route('/api/progress', methods=['GET'])
def api_progress():
progress = session.get('progress')
if not progress:
progress = 0
return {'progress': progress}
return {'progress': settings.PROGRESS}
32 changes: 32 additions & 0 deletions settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Client info for Spotify
import os

SPOTIFY_CLIENT_ID = os.environ['SPOTIFY_CLIENT_ID']
SPOTIFY_CLIENT_SECRET = os.environ['SPOTIFY_CLIENT_SECRET']

# Client info for Amazon Music
AMAZON_TOKEN = os.environ['AMAZON_TOKEN']
AMAZON_X_API_KEY = os.environ['AMAZON_X_API_KEY']

# Spotify API endpoints and configs
SPOTIFY_TOKEN_URL = 'https://accounts.spotify.com/api/token'
SPOTIFY_AUTH_URL = 'https://accounts.spotify.com/authorize'
SPOTIFY_BASE_ENDPOINT = 'https://api.spotify.com/v1'
SPOTIFY_SEARCH_ENDPOINT = f'{SPOTIFY_BASE_ENDPOINT}/search'
SPOTIFY_ME_ENDPOINT = f'{SPOTIFY_BASE_ENDPOINT}/me'
SPOTIFY_REDIRECT_URL = 'http://127.0.0.1:5000/callback'
SPOTIFY_STATE_KEY = 'spotify_auth_state'
SPOTIFY_API_SCOPES = [
'user-read-private',
'user-read-email',
'playlist-modify-private',
'playlist-modify-public',
]

# Amazon API endpoints
AMAZON_BASE_ENDPOINT = 'https://api.music.amazon.dev/v1'
AMAZON_TOKEN_URL = 'https://api.amazon.com/auth/o2/token'
AMAZON_ME_ENDPOINT = f'{AMAZON_BASE_ENDPOINT}/me'

# storage for progress
PROGRESS = 0
2 changes: 1 addition & 1 deletion templates/migrate.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
.then(response => response.json())
.then(data => document.getElementById('migration').value = data.progress)
.catch(error => console.error('Error:', error));
}, 5000);
}, 500);
</script>

<div class="media fadeitin">
Expand Down
89 changes: 79 additions & 10 deletions util.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import json
import logging
import random
import string
import time
import urllib

import requests
from flask import session

import settings
from settings import *


def generate_random_string(length):
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))


def query_artist_spotify(spotify_search_endpoint: str, artist=None):
def query_artist_spotify(artist=None):
"""Make request for data on `artist`."""

if artist is None:
Expand All @@ -24,17 +29,17 @@ def query_artist_spotify(spotify_search_endpoint: str, artist=None):
'Content-Type': 'application/json',
}

return requests.get(spotify_search_endpoint, params=payload, headers=headers)
return requests.get(SPOTIFY_SEARCH_ENDPOINT, params=payload, headers=headers)


def get_paginated_track_list(amazon_base_endpoint: str, amazon_token: str, amazon_x_api_key: str, playlist: dict, playlist_id: str, cursor: str = None):
def get_paginated_track_list(playlist: dict, playlist_id: str, cursor: str = None):
# get the playlist from amazon API (including tracks)
url = f"{amazon_base_endpoint}/playlists/{playlist_id}/tracks"
url = f"{AMAZON_BASE_ENDPOINT}/playlists/{playlist_id}/tracks"
if cursor:
url += f"?cursor={cursor}"
headers = {
"Authorization": f"Bearer {amazon_token}",
"x-api-key": amazon_x_api_key,
"Authorization": f"Bearer {AMAZON_TOKEN}",
"x-api-key": AMAZON_X_API_KEY,
"Content-Type": "application/json",
}

Expand All @@ -55,15 +60,79 @@ def get_paginated_track_list(amazon_base_endpoint: str, amazon_token: str, amazo
return playlist


def create_spotify_playlist(spotify_me_endpoint: str, name: str):
def create_spotify_playlist(name: str):
"""Creates a spotify playlist with the given name if it doesn't exist yet."""
# get current user's id
headers = {
'Authorization': f"Bearer {session.get('spotify_access_token')}",
'Accept': 'application/json',
'Content-Type': 'application/json',
}
resp = requests.get(SPOTIFY_ME_ENDPOINT, headers=headers)
user_id = json.loads(resp.text)['id']

endpoint = f"{SPOTIFY_BASE_ENDPOINT}/users/{user_id}/playlists"
headers = {
"Authorization": f"Bearer {session['spotify_access_token']}",
"Content-Type": "application/json",
}

data = {
"name": name,
"description": "Playlist migrated using Amazify",
"public": False
}

resp = requests.get(spotify_me_endpoint, headers=headers)
data = json.loads(resp.text)
print(data)
response = requests.post(endpoint, headers=headers, json=data)
if response.status_code != 201:
logging.error("could not create new playlist in spotify")
logging.error(response.text)

playlist_id = json.loads(response.text)['id']
return playlist_id


def add_tracks_to_spotify_playlist(spotify_access_token: str, playlist_id: str, tracks: list[dict]):
count = 0
for track in tracks:
count += 1
# first get track id
logging.debug(f"Searching for track: {track['artist']} - {track['title']}")
url = f'{SPOTIFY_BASE_ENDPOINT}/search'
params = {
'q': f'remaster%20track:{track["title"]}%20artist:{track["artist"]}',
'type': 'track',
'limit': 2,
}
headers = {
'Authorization': f'Bearer {spotify_access_token}'
}
response = requests.get(url, params=params, headers=headers)
results = json.loads(response.text)['tracks']['items']
# sort results by popularity and use most popular
result = sorted(results, key=lambda d: d['popularity'])[0]
spotify_track_id = result['id']
# might return inaccurate results if track is not actually present in spotify
logging.debug(f'Found track on spotify: {result["artists"][0]["name"]} - {result["name"]}')

# add track to playlist
url = f'{SPOTIFY_BASE_ENDPOINT}/playlists/{playlist_id}/tracks'
headers = {
'Authorization': f'Bearer {spotify_access_token}',
'Content-Type': 'application/json',
}
data = {
'uris': [
f'spotify:track:{spotify_track_id}'
],
'position': 0
}

response = requests.post(url, headers=headers, json=data)
if response.status_code != 201:
logging.error("couldn't add track {track['artist'] - track['title']} to playlist")
logging.error(response.text)

# write progress to session
count += 1
settings.PROGRESS = count / len(tracks) * 100

0 comments on commit 4572368

Please sign in to comment.