-
Notifications
You must be signed in to change notification settings - Fork 0
/
playlister.py
executable file
·168 lines (131 loc) · 5.48 KB
/
playlister.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/env python3
import json
import os
import subprocess
import openai
import spotipy
from termcolor import colored
import sp_auth
from config import ACCESS_TOKEN_FILE, DATA_PATH
from playlists_db import PlaylistManager
from openai import OpenAI
api_key = os.getenv("OPENAI_API_KEY")
client = OpenAI(api_key=api_key)
openai.api_key = api_key
def play_playlist(sp, tracks_list, device_id, playlist_name):
"""
Play a playlist on the given device using the Spotify API.
If the given 'playlist_name' does not exist in the user's playlists, it will be created.
if it does exist, it will just be played.
:param sp:
:param tracks_list: the playlist json
:param device_id:
:param playlist_name: the name of the playlist the user gave
"""
# Get the user's playlists
playlists = sp.current_user_playlists()
# Check if playlist with the given name already exists
playlist_id = None
for item in playlists['items']:
if item['name'] == playlist_name:
playlist_id = item['id']
break
user_id = sp.me()['id']
# If no existing playlist is found, create a new one
if playlist_id is None:
track_uris = []
for track in tracks_list["playlist"]:
results = sp.search(q='track:{} artist:{}'.format(track['song_name'], track['artist_name']), type='track')
if results['tracks']['items']:
track_uris.append(results['tracks']['items'][0]['uri'])
print(colored(f"Creating playlist {playlist_name} with {len(track_uris)} tracks", "yellow"))
playlist = sp.user_playlist_create(user=user_id, name=playlist_name)
playlist_id = playlist['id']
sp.user_playlist_replace_tracks(user=user_id, playlist_id=playlist_id, tracks=track_uris)
# Start playback on the selected device for the given playlist
sp.start_playback(device_id=device_id, context_uri=f'spotify:playlist:{playlist_id}')
def generate_tracks_list(playlist_description: str) -> dict:
"""
Generate a list of tracks based on the user's 'playlist_description' using GPT
:param playlist_description:
:return: a JSON describing the tracks list
"""
prompt = f"""
{playlist_description}. Generate a list of 15 songs in the format of a JSON with song name and artist name:
Use the following JSON format:
{{
"playlist":
[
{{"song_name": "The long and winding road", "artist_name": "The Beatles"}},
{{"song_name": "Sweet Child o' Mine", "artist_name": "Guns N' Roses"}},
]
}}
"""
# Call the GPT-4 model to generate a response
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "You are a knowledgeable AI trained to generate music playlists."},
{"role": "user", "content": prompt}
]
)
# Extract the assistant's reply
assistant_reply = response.choices[0].message.content
# Parse JSON and return it
playlist = json.loads(assistant_reply)
return playlist
def setup_spotify():
# Read access token from creds/access_token.txt
# to generate this file, run sp_auth.py
with open(ACCESS_TOKEN_FILE, "r") as f:
access_token = f.read()
return spotipy.Spotify(auth=access_token)
def authorize_spotify():
sp_auth.run_flow()
def main():
# if token file does not exist
if not os.path.exists(ACCESS_TOKEN_FILE):
print(colored("Running authorization flow", "red", attrs=["bold"]))
authorize_spotify()
exit(1)
sp = setup_spotify()
# Ask the user for their desired playlist
pm = PlaylistManager(DATA_PATH)
print(colored("Here are your playlists:", "green"))
playlists = pm.list_playlists()
for i, playlist in enumerate(playlists, 0):
print(f"{i}. {playlist}")
playlist_description = input(
colored("\nEnter an playlist number OR a description for a new playlist you want:\n", "green", attrs=["bold"]))
print(colored("Opening your spotify desktop app", "yellow", attrs=["bold"]))
command = "/Applications/Spotify.app/Contents/MacOS/Spotify"
subprocess.Popen(command)
if playlist_description.isdigit():
# Load old playlist
playlist = pm.load_playlist(int(playlist_description))
playlist_description = pm.get_playlist_name(int(playlist_description))
print(colored(f"Loading {playlist_description}...", "yellow", attrs=["bold"]))
else:
# Generate new playlist
print(colored("Generating playlist...", "yellow", attrs=["bold"]))
playlist = generate_tracks_list(playlist_description)
pm.save_playlist(playlist_description, playlist)
print(colored("Playing:", "green"))
text_list = playlist_json_to_text(playlist)
print(colored(text_list, "yellow"))
try:
devices = sp.devices()
device_id = devices['devices'][0]['id'] # get the first device
print(colored("\n\nPlaying...", "yellow", attrs=["bold"]))
play_playlist(sp, playlist, device_id, playlist_description)
except spotipy.exceptions.SpotifyException:
print(colored("Your spotify token has expired, running authorization flow", "red", attrs=["bold"]))
authorize_spotify()
def playlist_json_to_text(playlist: dict) -> str:
text_list = ""
for i, song in enumerate(playlist["playlist"], start=1):
text_list += f"{i}. {song['song_name']} by {song['artist_name']}\n"
return text_list
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
main()