Skip to content

Instantly share code, notes, and snippets.

@xthesaintx
Last active December 21, 2022 00:41
Show Gist options
  • Save xthesaintx/1aa8fd2e3b60cca345962279b0ae0f66 to your computer and use it in GitHub Desktop.
Save xthesaintx/1aa8fd2e3b60cca345962279b0ae0f66 to your computer and use it in GitHub Desktop.
Creates a spotify playlist based on a track ID
#!/usr/bin/env python3
####
## Replace XXXX with your details
####
import spotipy
import datetime
import pprint
import os
import sys
# from prompter import prompt, yesno
from spotipy.oauth2 import SpotifyOAuth
# Import the os module
import os
# Get the current working directory
os.chdir ('/home/pi/Scripts')
cwd = os.getcwd()
# Print the current working directory
# print("Current working directory: {0}".format(cwd))
# Print the type of the returned object
# print("os.getcwd() returns an object of type: {0}".format(type(cwd)))
os.environ["PYTHONIOENCODING"] = "utf-8"
logtext=""
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id="XXXX",
client_secret="XXXX",
redirect_uri="http://localhost:8000",
username='XXXX',
scope="user-library-read playlist-modify-public playlist-modify-private"))
#### FUNCTIONS ####
## PLAYLISTS AND ADDING TRACK
def does_playlist_exist(u,s,pl):
x = s.user_playlists(u)
for x in x['items']:
if x['name'] == pl:
return True
return False
def create_playlist(u, s, pl, desc):
s.trace = False
s.user_playlist_create(u, pl, True, description=desc) # True indicates it is a public playlist
def get_playlist_id(u,s,pl):
x = s.user_playlists(u)
for x in x['items']:
if x['name'] == pl:
return x['id']
# Gets the track ids in this weeks regular Discover Weekly Playlist
def get_track_ids(tracks):
global logtext
track_ids=[]
for i, item in enumerate(tracks['items']):
track = item['track']
track_ids.append(track['id'])
logtext = logtext+("%d - %s - <i>%s</i><br>" % (i, track['artists'][0]['name'], track['name']))
return track_ids
## TRACK ID AND INFO
def id_to_spid(i):
i = sp.track(i)
i = i["uri"]
i_out = [i]
return i_out
def id_to_info(id_info_in):
id_info_out = sp.track(id_info_in)
return id_info_out
def imagefromid(img_id):
i_id = id_to_info(img_id)
return (i_id["album"]["images"][1]["url"])
def print_from_id(print_id):
tpid = id_to_info(print_id)
print (tpid["id"],end=" - <i>", flush=True)
print (tpid["name"],end="</i> - ", flush=True)
print (tpid["album"]["artists"][0]["name"], flush=True)
print ("<br>", flush=True)
def get_genres(id_raw):
t_id = id_to_info(id_raw)
### ARTIST ###
artist_id = sp.artist(t_id["album"]["artists"][0]["id"])
return (artist_id["genres"])
## TRACK RECs
def get_rec (id_raw,quant,genres,chk_list):
id_in =id_to_spid(id_raw)
t_list =[]
i = 0
x = sp.recommendations(seed_artist=None,seed_tracks=id_in,limit=seed_limit,country=seed_region,seed_genres=genres)
for track in x["tracks"]:
if track["id"] not in track_list and track["id"] not in t_list and track["id"] not in chk_list and i < quant:
i +=1
t_list.append(track["id"])
# print (i, end="", flush=True)
if i == quant:
return t_list
def tiertest(z):
r = [0,0,0,0,0,0]
tval = 200
r[0] = z[0]
r[1] = r[0] * z[1]
r[2] = r[1] * z[2]
r[3] = r[2] * z[3]
if r[3]!=0:
r[4] = r[3] * z[4]
if r[4]!=0:
r[5] = r[4] * z[5]
rtot = sum (r)
# print (z)
# print (r)
# print (rtot)
if rtot>tval:
return False
else:
return True
##### DEFINE REC SEED VARIABLES #####
## SET VARIABLES
# SET your Region
seed_region ="NZ"
# Set the pool size of recomendations
seed_limit = 20
# User for where the playlist is being created, should be the same as the Auth abover
username = 'XXXX'
## Tiers
# Tier width defines how many recs to get at each level for each seed
# 5,3,2 - 5 tracks from seed (5), 3 tracks from those 5 (15), and 2 from those (30); 50 total
tier1=[]
tier2=[]
tier3=[]
tier4=[]
tier5=[]
tier6=[]
# tier_width = [5,3,2]
tier_width = [3,4,7,0,0,0]
seed_track_id = "7xpw5FSNYXKfdQSBuO0THv"
c_pl = True
g_lock = True
treeonly = False
### SYSTEM ARGS
## python.py seed_track_id g_lock[1|0] c_pl[1|0] tier_width[0 0 0 0 0 0] treeonly[1|0]
# eg. python.py 7xpw5FSNYXKfdQSBuO0THv 1 1 3 4 7 0 0 0 0
# Track ID
if len(sys.argv) > 0:
seed_track_id = sys.argv[1]
# Genre lock
if len(sys.argv) > 1:
g_lock = bool(int(sys.argv[2]))
# Create list
if len(sys.argv) > 2:
c_pl = bool(int(sys.argv[3]))
# Tier width
if len(sys.argv) > 3:
tier_width[0] = int(sys.argv[4])
tier_width[1] = int(sys.argv[5])
tier_width[2] = int(sys.argv[6])
if len(sys.argv) > 6:
tier_width[3] = int(sys.argv[7])
tier_width[4] = int(sys.argv[8])
tier_width[5] = int(sys.argv[9])
if len(sys.argv) > 9:
treeonly = bool(int(sys.argv[10]))
## output 1 level tree ##
if treeonly:
sp = ""
t1txt =""
print ("<pre>Seed Track")
# for x1 in range(int(tier_width[0])):
print ("|")
print ("["+str(tier_width[0])+"]--> Track")
# if x1 == int(tier_width[0])-1:
t1txt=" "
# else:
# t1txt="|"
for x2 in range(int(tier_width[1])):
print(t1txt+" |---> Track")
if x2 == int(tier_width[1])-1:
t2txt=" "
else:
t2txt="|"
for x3 in range(int(tier_width[2])):
print(t1txt+" "+t2txt+" |---> Track")
if x3 == int(tier_width[2])-1:
t3txt=" "
else:
t3txt="|"
for x4 in range(int(tier_width[3])):
print(t1txt+" "+t2txt+" "+t3txt+" |---> Track")
if x4 == int(tier_width[3])-1:
t4txt=" "
else:
t4txt="|"
for x5 in range(int(tier_width[4])):
print(t1txt+" "+t2txt+" "+t3txt+" "+t4txt+" |---> Track")
if x5 == int(tier_width[4])-1:
t5txt=" "
else:
t5txt="|"
for x6 in range(int(tier_width[5])):
print(t1txt+" "+t2txt+" "+t3txt+" "+t4txt+" "+t5txt+" |---> Track")
print("</pre>")
exit()
if tiertest(tier_width) == False:
tier_width = [3,4,7,0,0,0]
print ("<br>error tier_width input exceeds maximum tracks, resetting to default<br><br>")
# List that will output to playlist
track_list = [seed_track_id]
trackimg = imagefromid(seed_track_id)
print ("<img src="+trackimg+" ><br><br>")
print ("<strong>Seed Track</strong>: ", flush=True)
print_from_id(seed_track_id)
print ("<strong>Tier Width</strong>: ", flush=True)
print (tier_width)
print ("<br>")
print ("---<br>")
## Get Seed Genres
seed_genres = get_genres(seed_track_id)
print ("<strong>Genres</strong>", end=": ")
print (seed_genres)
print ("<br>")
# Set Genre Lock
if g_lock == True:
genres = seed_genres
### keep only 3 genres, seems to break with too many
if len(genres) > 2:
genres = genres[0:3]
print ("<strong>Trimmed</strong>:")
print (genres)
print ("<br>")
print ("<strong>Genre Lock</strong>: On")
else:
genres = ""
print ("<strong>Genre Lock</strong>: Off")
print ("<br>---<br>")
#### RUN THROUGH EACH TIER ####
### def get_rec (id_raw,quant,genres,chk_list):
#### TIER 1 ####
tier1 = get_rec(seed_track_id,tier_width[0],genres,tier1)
track_list.extend(tier1)
#### TIER 2 ####
for t1 in tier1:
tier2.extend(get_rec(t1,tier_width[1],genres,tier2))
track_list.extend(tier2)
#### TIER 3 ####
for t2 in tier2:
tier3.extend(get_rec(t2,tier_width[2],genres,tier3))
track_list.extend(tier3)
#### TIER 4 ####
if tier_width[3] != 0:
for t3 in tier3:
tier4.extend(get_rec(t3,tier_width[3],genres,tier4))
track_list.extend(tier4)
#### TIER 5 ####
if tier_width[4] != 0 :
for t4 in tier4:
tier5.extend(get_rec(t4,tier_width[4],genres,tier5))
track_list.extend(tier5)
#### TIER 6 ####
if tier_width[5] != 0 :
for t5 in tier5:
tier6.extend(get_rec(t5,tier_width[5],genres,tier6))
track_list.extend(tier6)
#### OUTPUT PLAYLIST ####
today = datetime.datetime.now()
date_time = today.strftime("%d/%m/%Y, %H:%M:%S")
playlistname = str(id_to_info(seed_track_id)["name"])+ " [PY]"
desc_in = "Tiers: "+str(tier_width)+" - Date: "+ str(date_time)
tpln = str(playlistname)
pl_suffix = 1
if c_pl == True:
while does_playlist_exist(username,sp,tpln):
#
if pl_suffix < 10:
sfx = "0"+str(pl_suffix)
else:
sfx = str(pl_suffix)
#
tpln = playlistname+" ["+sfx+"]"
pl_suffix +=1
playlistname = tpln
create_playlist(username, sp, playlistname, desc_in)
plid = get_playlist_id(username,sp,playlistname)
if len(track_list)>100:
results = sp.playlist_replace_items(plid, track_list[:100])
results = sp.playlist_add_items(plid, track_list[100:])
# print (track_list[:100])
# print (track_list[100:])
else:
results = sp.playlist_replace_items(plid, track_list)
print ("<strong>--- "+playlistname+" [Created] ---</strong>")
else:
print ("<strong>--- "+playlistname+" [Test] ---</strong>")
print ("<br>")
for t in track_list:
print_from_id(t)
@xthesaintx
Copy link
Author

xthesaintx commented Dec 20, 2022

New revision runs from CLI args... I'm using this from a WEB UI that I can set the tier widths with sliders.

"python.py seed_track_id g_lock[1|0] c_pl[1|0] tier_width[0 0 0 0 0 0] treeonly[1|0]"
eg. python.py 7xpw5FSNYXKfdQSBuO0THv 1 1 3 4 7 0 0 0 0

"g_lock" genre lock 1 or 0
"c_pl" create playlist 1 (creates a playlist in your account) or 0 (generates a track list but doesn't create a playlist)
"treeonly" will just output the tree structure to a log file and not generate a playlist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment