Quick actions

cmd+k|ctrl+k

Navigation

Languages

Add Random Song To Queue (SPOTIFY API)

Snippet info

Language

Lua

Visibility

public

Author

likeableemmec

Created

2025-01-09T15:24:15.03415Z

Updated

2025-04-18T09:42:34.668994Z

--[[
spotify recently broke this! so now it's 10x harder to use...
erm... just get a token with the scopes "user-library-read user-read-playback-state user-read-currently-playing user-modify-playback-state" and you'll be grand
]]

--[[
ERROR SOLUTIONS:
404 Error (Not Found): make sure your spotify is open
Attempt to concatenate string with nil: make sure you provided a third run argument
]]

local CLICK_COOLDOWN = 10 -- cooldown on the amount of times a player can click (in seconds)

script.Name = "make me listen to a spotify song"

-- creating
local main_part = Instance.new("Part")
main_part.Name = "Main"
main_part.Anchored = true
main_part.Locked = true
main_part.Position = Vector3.new(0, 2.5, 15)
main_part.Size = Vector3.new(5, 5, 0)
--main_part.Transparency = 1
main_part.Color = Color3.fromRGB(27, 42, 53)
main_part.Parent = script

local surface_gui = Instance.new("SurfaceGui")
surface_gui.PixelsPerStud = 60
surface_gui.SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud
surface_gui.Parent = main_part

local background = Instance.new("Frame")
background.Name = "Background"
background.BackgroundTransparency = 1
--background.BackgroundColor3 = Color3.fromRGB(27, 42, 53)
background.BorderSizePixel = 0
background.Size = UDim2.fromScale(1, 1)
background.Parent = surface_gui

local shuffle_background = Instance.new("Frame")
shuffle_background.Name = "ShuffleBackground"
shuffle_background.BackgroundTransparency = 1
shuffle_background.Position = UDim2.fromScale(0, 1/3)
shuffle_background.Size = UDim2.fromScale(1, 1/3)
shuffle_background.Parent = background

local recently_queued do
	recently_queued = Instance.new("TextLabel")
	recently_queued.Name = "RecentlyQueued"
	recently_queued.AnchorPoint = Vector2.new(0, 1)
	recently_queued.BackgroundTransparency = 1
	recently_queued.Position = UDim2.fromScale(0, 1)
	recently_queued.Size = UDim2.fromScale(1, 0.075)
	recently_queued.FontFace = Font.new("rbxasset://fonts/families/Inconsolata.json", Enum.FontWeight.Regular, Enum.FontStyle.Normal)
	recently_queued.TextColor3 = Color3.new(1,1,1)
	recently_queued.Text = "Recently queued: None"
	recently_queued.TextWrapped = true
	recently_queued.Parent = shuffle_background

	local cooldown = Instance.new("TextLabel")
	cooldown.Name = "Cooldown"
	cooldown.BackgroundTransparency = 1
	cooldown.Size = UDim2.fromScale(1, 0.075)
	cooldown.FontFace = Font.new("rbxasset://fonts/families/Inconsolata.json", Enum.FontWeight.Regular, Enum.FontStyle.Normal)
	cooldown.TextColor3 = Color3.new(1,1,1)
	cooldown.Text = `({CLICK_COOLDOWN} second cooldown per player)`
	cooldown.TextWrapped = true
	cooldown.Parent = shuffle_background
end

local description = Instance.new("TextLabel")
description.Name = "Description"
description.BackgroundTransparency = 1
description.Size = UDim2.fromScale(1, 1/3) -- 1/3 = 0.333...
description.TextColor3 = Color3.new(1,1,1)
description.FontFace = Font.new("rbxasset://fonts/families/Inconsolata.json", Enum.FontWeight.Regular, Enum.FontStyle.Normal)
description.Text = "Add a RANDOM SONG (yes, ANY song) to my queue (please don't):"
description.TextSize = 14
description.TextWrapped = true
description.Parent = background

local shuffle_click do
	local shuffle_part = Instance.new("Part")
	shuffle_part.Name = "ShufflePart"
	shuffle_part.Anchored = true
	shuffle_part.Size = Vector3.new(3.767, 1.266, 0.05)
	shuffle_part.Position = Vector3.new(0, 2.5, 14.975)
	shuffle_part.Transparency = 1
	shuffle_part.Parent = main_part

	shuffle_click = Instance.new("ClickDetector")
	shuffle_click.Parent = shuffle_part

	local shuffle_button = Instance.new("TextButton")
	shuffle_button.Name = "Shuffle"
	shuffle_button.BackgroundColor3 = Color3.new(0, 1, 0)
	shuffle_button.AnchorPoint = Vector2.new(.5, .5)
	shuffle_button.Position = UDim2.fromScale(.5, .5)
	shuffle_button.Size = UDim2.fromScale(0.75, 0.75)
	shuffle_button.TextColor3 = Color3.new(1,1,1)
	shuffle_button.Text = "Yes, I hate you. Add a song to the queue."
	shuffle_button.FontFace = Font.new("rbxasset://fonts/families/Inconsolata.json", Enum.FontWeight.Bold, Enum.FontStyle.Italic)
	shuffle_button.TextStrokeTransparency = 0
	shuffle_button.TextSize = 14
	shuffle_button.TextWrapped = true
	shuffle_button.Parent = shuffle_background
end

local currently_playing = Instance.new("TextLabel")
currently_playing.Name = "CurrentlyPlaying"
currently_playing.BackgroundTransparency = 1
currently_playing.Position = UDim2.fromScale(0, 2/3) -- 2/3 = 0.666...
currently_playing.Size = UDim2.fromScale(1, 1/3)
currently_playing.TextColor3 = Color3.new(1,1,1)
currently_playing.Text = "Currently playing:\nNone\nAlbum: None\nArtist: None"
currently_playing.FontFace = Font.new("rbxasset://fonts/families/Inconsolata.json", Enum.FontWeight.Regular, Enum.FontStyle.Italic)
currently_playing.TextSize = 14
currently_playing.TextWrapped = true
currently_playing.Parent = background

local queue_background do
	local queue_part = Instance.new("Part")
	queue_part.Name = "Queue"
	queue_part.Color = Color3.fromRGB(27, 42, 53)
	queue_part.Size = Vector3.new(3.5, 4.5, 0)
	queue_part.Position = Vector3.new(-4.75, 2.75, 15)
	queue_part.Anchored = true
	queue_part.Parent = script
	
	local queue_gui = Instance.new("SurfaceGui")
	queue_gui.PixelsPerStud = 60
	queue_gui.SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud
	queue_gui.Parent = queue_part
	
	queue_background = Instance.new("ScrollingFrame")
	queue_background.Name = "QueueBackground"
	queue_background.BackgroundTransparency = 1
	queue_background.Position = UDim2.fromScale(0, 0)
	queue_background.Size = UDim2.fromScale(1, 1)
	queue_background.AutomaticCanvasSize = Enum.AutomaticSize.XY
	queue_background.VerticalScrollBarInset = Enum.ScrollBarInset.ScrollBar
	queue_background.ScrollBarThickness = 6
	queue_background.BorderSizePixel = 0
	queue_background.CanvasSize = UDim2.fromScale(0, 0)
	queue_background.Parent = queue_gui
	
	local grid_layout = Instance.new("UIGridLayout")
	grid_layout.CellPadding = UDim2.fromScale(0, 0)
	grid_layout.CellSize = UDim2.fromScale(1, 0.2)
	grid_layout.SortOrder = Enum.SortOrder.LayoutOrder
	grid_layout.Parent = queue_background
	
	local description = Instance.new("TextLabel")
	description.Name = "Description"
	description.BackgroundTransparency = 1
	-- grid_layout controls position and size
	description.LayoutOrder = -2
	description.FontFace = Font.new("rbxasset://fonts/families/Inconsolata.json", Enum.FontWeight.Regular, Enum.FontStyle.Italic)
	description.Text = "(Songs in bold and italics are songs added into the queue via this script)"
	description.TextColor3 = Color3.new(1, 1, 1)
	description.TextWrapped = true
	description.TextSize = 14
	description.Parent = queue_background
	
	local description_padding = Instance.new("UIPadding")
	description_padding.PaddingLeft = UDim.new(.05, 0)
	description_padding.PaddingRight = UDim.new(.05, 0)
	description_padding.Parent = description
	
	local top_song = Instance.new("TextLabel")
	top_song.Name = "TopSong"
	top_song.BackgroundTransparency = 1
	-- grid_layout controls position and size
	top_song.LayoutOrder = -1
	top_song.FontFace = Font.new("rbxasset://fonts/families/Inconsolata.json", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
	top_song.Text = "Songs in queue:"
	top_song.TextColor3 = Color3.new(1, 1, 1)
	top_song.TextWrapped = true
	top_song.TextSize = 20
	top_song.Parent = queue_background
end

-- main
local http_service = game:GetService("HttpService")
--local token = http_service:JSONDecode(http_service:GetAsync("https://open.spotify.com/get_access_token?reason=transport&productType=web_player", false, {
--	["App-platform"] = "WebPlayer",
--	["cookie"] = "sp_dc=" .. ...
--}))
local access_token = ...
local recently_added_tracks = {}
local function get_random_song()
	local random_wildcards = {
		"pop",
		"dance pop",
		"rap",
		"pop rap",
		"post teen pop",
		"rock",
		"latin",
		"hip hop",
		"trap",
		"modern rock",
		"edm",
		"pop rock",
		"tropical house",
		"reggaeton",
		"melodic rap",
		"electropop",
		"latin pop",
		"classic rock",
		"mellow gold",
		"album rock",
		"southern hip hop",
		"soft rock",
		"post grunge",
		"indie pop",
		"alternative metal",
		"permanent wave",
		"r and b",
		"contemporary country",
		"neo mellow",
		"uk pop",
		"canadian pop",
		"electro house",
		"urban contemporary",
		"tropical",
		"alternative rock",
		"hard rock",
		"folk rock",
		"nu metal",
		"k pop",
		"country",
		"country road",
		"grupera",
		"trap latino",
		"rock en espanol",
		"adult standards",
		"viral pop",
		"alternative r and b",
		"atl hip hop",
		"german hip hop",
		"gangster rap",
		"regional mexican",
		"indietronica",
		"indie rock",
		"big room",
		"latin hip hop",
		"underground hip hop",
		"art rock",
		"banda",
		"dance rock",
		"europop",
		"roots rock",
		"new wave pop",
		"soul",
		"chamber pop",
		"country rock",
		"french hip hop",
		"indie folk",
		"modern alternative rock",
		"emo rap",
		"mexican pop",
		"indie poptimism",
		"house",
		"norteno",
		"art pop",
		"canadian hip hop",
		"latin alternative",
		"latin rock",
		"sertanejo universitario",
		"reggaeton flow",
		"pop urbaine",
		"toronto rap",
		"garage rock",
		"stomp and holler",
		"hip pop",
		"spanish pop",
		"latin arena pop",
		"boy band",
		"psychedelic rock",
		"lo fi beats",
		"pop punk",
		"metal",
		"pop edm",
		"neo soul",
		"singer songwriter",
		"funk",
		"folk",
		"progressive house",
		"funk carioca",
		"ranchera",
		"new wave",
	}
	local wildcard_selected = random_wildcards[math.random(1, #random_wildcards)]

	local track
	for i = 1, 50 do
		local response = http_service:RequestAsync({
			Url = `https://api.spotify.com/v1/search?q=genre:%22{wildcard_selected}%22&type=track&market=GB&offset={math.random(1, 200)}`,
			Method = "GET",
			Headers = {
				["scope"] = "user-read-private",
				["authorization"] = "Bearer " .. access_token;
			};
		})
		local song_info = http_service:JSONDecode(response.Body)["tracks"]["items"]
		local track_info = song_info[math.random(1, 20)]
		track = track_info
		break
	end
		
	return true, track
end

local function get_currently_playing_song()
	local response = http_service:RequestAsync({
		Url = "https://api.spotify.com/v1/me/player/currently-playing",
		Method = "GET",
		Headers = {
			--["scope"] = "user-read-currently-playing user-read-playback-state",
			["Content-Type"] = "application/json",
			["Authorization"] = "Bearer " .. access_token;
		};
	})
	local status = response.StatusCode
	if status == 200 then
		local data = http_service:JSONDecode(response.Body).item
		return true, data
	else
		if not response or response.Body == "" then
			local success
			repeat
				task.wait(1)
				success, response = get_currently_playing_song()
			until success
			return success, response
		else
			warn(response)
			return false, http_service:JSONDecode(response.Body).error.message
		end
	end
end

local function update_playing()
	local success, data = get_currently_playing_song()

	local song = data.name
	local album = data.album.name
	local artist = data.artists[1].name

	local text_to_show = "Currently listening to:"
	text_to_show ..= "\n" .. song
	text_to_show ..= "\nAlbum: " .. album
	text_to_show ..= "\nArtist: " .. artist
	currently_playing.Text = text_to_show
end

local function update_queue(uri)
	local response = http_service:PostAsync(
		`https://api.spotify.com/v1/me/player/queue?uri={uri}`,
		http_service:JSONEncode({}),
		Enum.HttpContentType.ApplicationJson,
		false, 
		{
			["Authorization"] = "Bearer " .. access_token;
		}
	)
	local status = response.StatusCode
	return true
end

local function generate_text(text : string, parent : Instance)
	local label = Instance.new("TextLabel")
	label.BackgroundTransparency = 1
	label.FontFace = Font.new("rbxasset://fonts/families/Inconsolata.json", Enum.FontWeight.Regular, Enum.FontStyle.Normal)
	label.TextColor3 = Color3.new(1,1,1)
	label.Text = text
	label.TextWrapped = true
	label.Parent = parent
	
	return label
end

local function read_and_update_queue_gui()
	local response = http_service:RequestAsync({
		Url = "https://api.spotify.com/v1/me/player/queue",
		Method = "GET",
		Headers = {
			["Content-Type"] = "application/json",
			["Authorization"] = "Bearer " .. access_token;
		};
	})
	local status = response.StatusCode
	if status == 200 then
		local body = http_service:JSONDecode(response.Body)
		local queue = body.queue
		for _, track in queue do
			--warn(track.name)
			local label = generate_text(track.name, queue_background)
			label.Name = "QueuedTrack"
			
			if table.find(recently_added_tracks, label.Text) then
				label.FontFace = Font.new("rbxasset://fonts/families/Inconsolata.json", Enum.FontWeight.SemiBold, Enum.FontStyle.Italic)
			end
			
			label.TextSize = 16
			label:SetAttribute("New", true)
		end
		
		for _, obj in queue_background:GetChildren() do
			if obj:IsA("UIGridLayout") then
				continue
			end

			if obj.Name == "TopSong" or obj.Name == "Description" then
				continue
			end

			if obj:GetAttribute("New") then
				obj:SetAttribute("New", nil)
				continue
			end
			
			local found_track = false
			for _, track in queue do
				local name = track.name
				if name == track then
					found_track = true
					break
				end
			end
			
			if found_track then
				continue
			end

			obj:Destroy()
		end
	else
		return http_service:JSONDecode(response.Body).error.message
	end
end

local function add_to_queue()
	local success, random_song = get_random_song()

	local song_id = random_song.uri
	update_queue(song_id)

	local song_name = random_song.name
	table.insert(recently_added_tracks, song_name)
	
	read_and_update_queue_gui()
	
	return song_name
end

local function play_sound(id)
	local sound = Instance.new("Sound")
	sound.SoundId = id
	sound.PlayOnRemove = true
	sound.Volume = 2
	sound.Parent = shuffle_click.Parent
	sound:Destroy()
end

local player_cooldowns = {}
shuffle_click.MouseClick:Connect(function(plr)
	local cooldown_found = player_cooldowns[plr.Name]
	if cooldown_found then
		if tick() - cooldown_found > CLICK_COOLDOWN then
			player_cooldowns[plr.Name] = nil
		else
			return
		end
	end
	player_cooldowns[plr.Name] = tick()

	play_sound("rbxasset://sounds/button.wav")

	print(`{plr.Name} just clicked the button.`)
	local song_added = add_to_queue()
	recently_queued.Text = `Recently queued: {song_added}`
	print(`Song called '{song_added}' has been added to your queue!`)
end)

while true do
	update_playing()
	read_and_update_queue_gui()
	task.wait(1)
end
INFO