KEYLESS Epik Song player
Key RequiredUniversal

KEYLESS Epik Song player

Universal ScriptMrCookieMrCookie 16.6K views 6.3K executesUpdated Jul 17, 2025
5.0/ 5(2 votes)
20100% Positive
Lua Script•1040 lines (44288 chars)
--[[
	WARNING: Heads up! This script has not been verified by ScriptBlox. Use at your own risk!
]]
--[[
    WARNING: Heads up! This script has not been verified by ScriptBlox. Use at your own risk!
]]
-- Lua code to be injected client-side
-- This version is designed *exclusively* for execution via an external executor.
-- It avoids all LocalScript assumptions and directly manages UI creation and interaction.
-- All UI elements are explicitly created and parented under PlayerGui.
-- Addresses "nil value" errors by strictly defining variable scope and ensuring UI elements exist
-- before operations are attempted on them.
-- FIX: Popular songs now dynamically fetch names from MarketplaceService upon load.
-- FIX: Explicitly filters for Enum.AssetType.Audio.Value to prevent non-audio assets (like Places) from appearing.
-- FIX: Corrected use of Enum.AssetType.Audio.Value instead of Enum.AssetType.NameFromValue.
-- UPDATE: Reverted to a large, curated list of POPULAR_SONGS_IDS as true dynamic discovery
--         is not feasible due to Roblox API limitations for client-side scripts.
-- ADDITION: Added a disclaimer within the GUI and console regarding API limitations.
-- MODIFIED: Max retries for service acquisition set to unlimited.
-- MODIFIED: When "Popular Songs" button is pressed, the list is now *only* printed to the console.
-- REMOVED: Separate Popular Songs GUI panel.
-- Declare global services, using locals for faster access within functions
local Players = nil
local UserInputService = nil
local SoundService = nil
local MarketplaceService = nil
local RunService = nil

-- Robust service acquisition (crucial for injected environments)
local function getServiceRobustly(serviceName)
    local service = nil
    local retryDelay = 0.2 -- Increased delay

    -- Special handling for LocalPlayer and PlayerGui: Ensure they are ready
    if serviceName == "Players" then
        while true do -- Unlimited retries
            local success, srv = pcall(game.GetService, game, serviceName)
            if success and srv then
                local currentLocalPlayer = srv.LocalPlayer
                if currentLocalPlayer and currentLocalPlayer:FindFirstChild("PlayerGui") then
                    service = srv
                    break
                else
                    warn("Players service acquired, but LocalPlayer or PlayerGui not found yet. Retrying...")
                    task.wait(retryDelay)
                end
            else
                warn("Failed to get service 'Players'. Retrying in " .. retryDelay .. "s...")
                task.wait(retryDelay)
            end
        end
    else
        -- General service acquisition
        while true do -- Unlimited retries
            local success, srv = pcall(game.GetService, game, serviceName)
            if success and srv then
                service = srv
                break
            else
                warn("Failed to get service '" .. serviceName .. "'. Retrying in " .. retryDelay .. "s...")
                task.wait(retryDelay)
            end
        end
    end

    if not service then
        error("Critical Error: Could not get service '" .. serviceName .. "' after unlimited retries. Script cannot proceed.")
    end
    return service
end

-- Acquire all necessary services robustly at the very beginning
Players = getServiceRobustly("Players")
UserInputService = getServiceRobustly("UserInputService")
SoundService = getServiceRobustly("SoundService")
MarketplaceService = getServiceRobustly("MarketplaceService")
RunService = getServiceRobustly("RunService")

-- Configuration
local DEFAULT_VOLUME = 0.5
local BUTTON_HEIGHT = 30 -- Standard height for content buttons
local BUTTON_PADDING = 5 -- Padding between content buttons
local TEXTBOX_HEIGHT = 25 -- Height for input textboxes

-- Popular Song IDs for the "Spotify-like" UI - Now only IDs, names will be fetched
-- This is a large, curated list of known good Roblox audio IDs.
-- Note: A truly "infinite" list of *new* popular songs is not possible with client-side injection.
local POPULAR_SONGS_IDS = {
    -- Classic/Well-known Roblox sounds
    "9047105533", --No smoking
    "1837768745", -- Bossa Me
    "6023770176", -- Epic Battle Theme
    "1369372179", -- Chill Lofi Beat
    "5507380961", -- Upbeat Electro
    "142376916",  -- Piano Moment
    "184518041",  -- Funky Groove
    "6790933939", -- Adventure Awaits
    "5987340003", -- Dramatic Orchestral
    "153034965",  -- Cute Background Music
    "499525281",  -- Relaxing Forest Sounds
    "9190240974", -- Rainy Day Chill
    "607758713",  -- Cyberpunk Drive
    "1136450638", -- Mysterious Forest
    "177000858",  -- Summer Sunshine
    "1348398453", -- Jazz Club Night
    "292671044",  -- Sci-Fi Ambience
    "173574061",  -- Energetic Pop
    "2890690332", -- Dreamy Piano
    "164049870",  -- Classical Serenity
    "142270000",  -- Upbeat Corporate
    "173673551",  -- Happy Ukulele
    "1838634887", -- Arcade Funk
    "618218151",  -- Chillhop Beat
    "142323098",  -- Medieval Tavern
    "533221000",  -- Western Saloon
    -- Additional curated popular/diverse sounds (examples, replace with valid if needed)
    "6352000000", -- Placeholder: "Retro Wave"
    "5246700000", -- Placeholder: "Tropical Island"
    "10080000000",-- Placeholder: "Calm Forest Birds"
    "9207000000", -- Placeholder: "City Lights Drive"
    "6977000000", -- Placeholder: "Deep Space Ambient"
    "6729000000", -- Placeholder: "Spooky Mansion"
    "173574061",  -- Funky Fresh (Repeated, ensure unique valid IDs)
    "1838634887", -- Synthwave Dreams (Repeated, ensure unique valid IDs)
    "6023770176", -- Cybernetic Future (Repeated, ensure unique valid IDs)
    "5987340003", -- Cinematic Strings (Repeated, ensure unique valid IDs)
    "153034965",  -- Cute and Quirky (Repeated, ensure unique valid IDs)
    "499525281",  -- Nature Sounds River (Repeated, ensure unique valid IDs)
    "9190240974", -- Lo-fi Study (Repeated, ensure unique valid IDs)
    "607758713",  -- Neo Tokyo Drift (Repeated, ensure unique valid IDs)
    "1136450638", -- Enchanted Forest (Repeated, ensure unique valid IDs)
    "177000858",  -- Beach Vibes (Repeated, ensure unique valid IDs)
    -- More unique IDs (these are illustrative, real IDs would be needed)
    "8000000000", -- Example: Electro Dance
    "8000000001", -- Example: Relaxing Piano
    "8000000002", -- Example: Hard Rock Beat
    "8000000003", -- Example: Folk Acoustic
    "8000000004", -- Example: Ambient Space
    "8000000005", -- Example: Epic Orchestral Choir
    "8000000006", -- Example: Happy Chiptune
    "8000000007", -- Example: Mysterious Dungeon
    "8000000008", -- Example: Sci-Fi Alarm
    "8000000009", -- Example: Gentle Waves
    "8000000010", -- Example: City Street Ambience
    "8000000011", -- Example: Funky Bassline
    "8000000012", -- Example: Tropical Birdsong
    "8000000013", -- Example: Synthpop Anthem
    "8000000014", -- Example: Horror Drone
    "8000000015", -- Example: Upbeat Ukulele 2
    "8000000016", -- Example: Retro Arcade Game
    "8000000017", -- Example: Deep House Track
    "8000000018", -- Example: Peaceful Flute
    "8000000019", -- Example: Tribal Drums
    "8000000020", -- Example: Classical Guitar
    "8000000021", -- Example: Cybernetic Sounds
    "8000000022", -- Example: Chillwave Beat
    "8000000023", -- Example: Spooky Organ
    "8000000024", -- Example: Energetic Sports
    "8000000025", -- Example: Soothing Rain
    "8000000026", -- Example: Jazzy Saxophone
    "8000000027", -- Example: War Horn
    "8000000028", -- Example: Gentle Lullaby
    "8000000029", -- Example: Fast-paced Action
    "8000000030", -- Example: Relaxing Beach
    "8000000031", -- Example: Techno Club
    "8000000032", -- Example: Acoustic Fingerstyle
    "8000000033", -- Example: Space Engine Hum
    "8000000034", -- Example: Cinematic Impact
    "8000000035", -- Example: Happy Children's Song
    "8000000036", -- Example: Dark Ambient
    "8000000037", -- Example: Sci-Fi Computer
    "8000000038", -- Example: Ocean Storm
    "8000000039", -- Example: Future Funk
    "8000000040", -- Example: Eerie Forest
    "8000000041", -- Example: Classical Waltz
    "8000000042", -- Example: Distant Thunder
    "8000000043", -- Example: Groove R&B
    "8000000044", -- Example: Medieval Lute
    "8000000045", -- Example: Desert Wind
    "8000000046", -- Example: Pop Rock Anthem
    "8000000047", -- Example: Industrial Noise
    "8000000048", -- Example: Calm River Flow
    "8000000049", -- Example: Uplifting Trance
    "8000000050", -- Example: Jazzy Hip Hop
    -- NEW IDs ADDED PER USER REQUEST
    "5519565135", -- Mood – 24kGoldn
    "4728058875", -- Highway to Hell – AC/DC
    "146961487",  -- Thunderstruck – AC/DC
    "4606705490", -- Take On Me – A‑ha
    "5882199576", -- 34+35 – Ariana Grande
    "2071829884", -- God Is a Woman – Ariana Grande
    "521116871",  -- Say So – Doja Cat
    "4656290260", -- Like That (Instrumental) – Doja Cat
    "6991661856", -- Nightmare Music
    "1626996526", -- Windows XP Theme
    "142376088",  -- Raining Tacos
    "1801896",    -- Ima Firin My Lazer
    "1844750628",  -- Tower Blitz Boss theme
        -- NEW IDs ADDED PER USER REQUEST
    "5519565135", -- Mood – 24kGoldn
    "4728058875", -- Highway to Hell – AC/DC
    "146961487",  -- Thunderstruck – AC/DC
    "4606705490", -- Take On Me – A‑ha
    "5882199576", -- 34+35 – Ariana Grande
    "2071829884", -- God Is a Woman – Ariana Grande
    "521116871",  -- Say So – Doja Cat
    "4656290260", -- Like That (Instrumental) – Doja Cat
    "6991661856", -- Nightmare Music
    "1626996526", -- Windows XP Theme
    "142376088",  -- Raining Tacos
    "1801896",    -- Ima Firin My Lazer
    "1844750628", -- Tower Blitz Boss theme
    "1846088038", -- Morning Mood
    "9045766074",
    "9039981149",
    "1837879082",
    "9045766377",
    "1841647093",
    "1845497774",
    "5409360995",
    "9039661312",
    "1840684529",
    "9043887091",
    "1843404009",
    "1843404009",
    "1845341094",
}


-- Internal state (using _G for variables that need to persist and be globally accessible within the injected scope)
_G.currentSound = nil
_G.playlist = {} -- Stores {id = "rbxassetid://ID", name = "Custom Name"}
_G.currentPlaylistIndex = 0
_G.playlistPlaying = false
_G.loopCurrentSong = false
_G.loopAllPlaylist = false
_G.filteredPopularSongs = {} -- Stores only available popular songs after initial check, with fetched names

-- Function to play a sound
local function playSound(musicId, volume, loop)
    if _G.currentSound then
        _G.currentSound:Stop()
        _G.currentSound:Destroy()
        _G.currentSound = nil
    end

    if not musicId or musicId == "" then
        warn("Attempted to play with no music ID.")
        print("Please enter a valid Asset ID to play.")
        return
    end

    if not string.match(musicId, "^rbxassetid://%d+$") then
        print("Invalid Asset ID format for playing: " .. musicId .. ". Must be 'rbxassetid://' followed by a number.")
        return
    end

    local newSound = Instance.new("Sound")
    newSound.SoundId = musicId
    newSound.Volume = volume or DEFAULT_VOLUME
    newSound.Looped = loop or false
    newSound.Parent = SoundService

    _G.currentSound = newSound

    newSound.Ended:Connect(function()
        if _G.currentSound == newSound then
            if _G.loopCurrentSong then
                newSound:Play()
            elseif _G.playlistPlaying and #_G.playlist > 0 then
                _G.currentPlaylistIndex = _G.currentPlaylistIndex + 1
                if _G.currentPlaylistIndex > #_G.playlist then
                    if _G.loopAllPlaylist then
                        _G.currentPlaylistIndex = 1
                    else
                        _G.playlistPlaying = false
                        print("Playlist finished!")
                        newSound:Destroy()
                        _G.currentSound = nil
                        return
                    end
                end
                local nextSong = _G.playlist[_G.currentPlaylistIndex]
                if nextSong then
                    print("Playing next song in playlist: " .. (nextSong.name or nextSong.id:gsub("rbxassetid://", "")))
                    playSound(nextSong.id, DEFAULT_VOLUME, false)
                else
                    newSound:Destroy()
                    _G.currentSound = nil
                end
            else
                newSound:Destroy()
                _G.currentSound = nil
            end
        end
    end)

    newSound:Play()
    print("Now playing: " .. musicId:gsub("rbxassetid://", ""))
end

local function stopSound()
    if _G.currentSound then
        _G.currentSound:Stop()
        _G.currentSound:Destroy()
        _G.currentSound = nil
        _G.playlistPlaying = false
        _G.currentPlaylistIndex = 0
        print("Music stopped.")
    else
        print("No music is currently playing.")
    end
end

-- Function to check if an asset is available and get its name (can be used independently)
local function getAssetInfo(assetIdString)
    local assetIdNumber = tonumber(assetIdString)
    if not assetIdNumber then
        warn("Invalid asset ID format for info lookup: " .. tostring(assetIdString))
        return false, "Invalid_ID", "Asset_" .. tostring(assetIdString)
    end

    local success, info = pcall(MarketplaceService.GetProductInfo, MarketplaceService, assetIdNumber, Enum.InfoType.Asset)
    if success and info then
        -- Explicitly check for Audio AssetType AND availability
        if info.AssetTypeId == Enum.AssetType.Audio.Value and not info.IsRemoved then
            -- Filter out (Removed for copyright) names
            if string.find(info.Name, "(Removed for copyright)", 1, true) then
                warn("Asset ID " .. tostring(assetIdNumber) .. " is an audio asset but its name contains '(Removed for copyright)'. Skipping.")
                return false, "Copyright_Removed", info.Name or "Asset_" .. tostring(assetIdNumber)
            end
            return true, info.Name, info.Name or "Asset_" .. tostring(assetIdNumber)
        else
            -- If it's not Audio or is removed, consider it unavailable for our purpose
            warn("Asset ID " .. tostring(assetIdNumber) .. " is not an available Audio asset or is removed. (AssetTypeId: " .. tostring(info.AssetTypeId) .. ", Removed: " .. tostring(info.IsRemoved) .. ")")
            return false, "Unavailable", info.Name or "Asset_" .. tostring(assetIdNumber)
        end
    end
    -- Failed pcall or info is nil
    warn("Failed to get product info for asset ID: " .. tostring(assetIdNumber))
    return false, "Failed_Lookup", "Asset_" .. tostring(assetIdNumber)
end


local function handlePlaylistAdd(id, name)
    local fullId = "rbxassetid://" .. id
    local songName = name

    if not songName or string.len(songName) == 0 then
        -- If no custom name, try to fetch it
        local isAvailable, fetchedName = getAssetInfo(id)
        songName = fetchedName
        if not isAvailable then
            print("Could not add song (ID: " .. id .. ") to playlist: Asset not found or not an audio (or removed for copyright).")
            return false
        end
    end

    if not string.match(fullId, "^rbxassetid://%d+$") then
        print("Invalid Asset ID format: " .. id .. ". Must be a number.")
        return false
    end

    local alreadyExists = false
    for _, song in ipairs(_G.playlist) do
        if song.id == fullId then
            alreadyExists = true
            print("Song " .. (songName) .. " (ID: " .. id .. ") already in playlist.")
            break
        end
    end

    if not alreadyExists then
        table.insert(_G.playlist, {id = fullId, name = songName})
        print("Added to playlist: " .. songName .. " (ID: " .. id .. ")")
        return true
    end
    return false
end

local function handlePlaylistRemove(query)
    local removedCount = 0
    for i = #_G.playlist, 1, -1 do
        local song = _G.playlist[i]
        if string.lower(song.name) == string.lower(query) or song.id == "rbxassetid://" .. query then
            table.remove(_G.playlist, i)
            removedCount = removedCount + 1
            print("Removed from playlist: " .. (song.name or song.id:gsub("rbxassetid://", "")))
        end
    end
    if removedCount == 0 then
        print("Could not find '" .. query .. "' in playlist. Try exact ID or name.")
    else
        print("Removed " .. removedCount .. " song(s) for '" .. query .. "'.")
    end

    if _G.currentSound then
        local currentSoundId = _G.currentSound.SoundId
        local foundCurrentInPlaylist = false
        for _, song in ipairs(_G.playlist) do
            if song.id == currentSoundId then
                foundCurrentInPlaylist = true
                break
            end
        end

        if not foundCurrentInPlaylist and _G.playlistPlaying then
            stopSound()
            if #_G.playlist > 0 then
                _G.currentPlaylistIndex = 1
                _G.playlistPlaying = true
                local nextSong = _G.playlist[_G.currentPlaylistIndex]
                if nextSong then
                    print("Resuming playlist playback after removal with: " .. (nextSong.name or nextSong.id:gsub("rbxassetid://", "")))
                    playSound(nextSong.id, DEFAULT_VOLUME, false)
                else
                    _G.playlistPlaying = false
                    print("Playlist empty after removal, stopping playback.")
                end
            else
                _G.playlistPlaying = false
                print("Playlist empty after removal, stopping playback.")
            end
        elseif not foundCurrentInPlaylist and not _G.playlistPlaying then
             stopSound()
        end
    end
end

local function handlePlaylistList()
    print("--- Current Playlist ---")
    if #_G.playlist == 0 then
        print("Playlist is empty.")
    else
        for i, song in ipairs(_G.playlist) do
            local prefix = (_G.currentPlaylistIndex == i and _G.playlistPlaying) and " (Now Playing)" or ""
            local songInfo = string.format("%d. %s (ID: %s)%s", i, song.name, song.id:gsub("rbxassetid://", ""), prefix)
            print(songInfo)
        end
    end
    print("------------------------")
    if _G.currentSound and not _G.currentSound.IsPlaying and not _G.playlistPlaying then
        print("Currently paused (manual): " .. _G.currentSound.SoundId:gsub("rbxassetid://", ""))
    elseif _G.currentSound and not _G.playlistPlaying then
        print("Currently playing (manual): " .. _G.currentSound.SoundId:gsub("rbxassetid://", ""))
    elseif not _G.currentSound then
        print("No music currently playing.")
    end
end

local function wipePlaylist()
    stopSound()
    _G.playlist = {}
    _G.currentPlaylistIndex = 0
    _G.playlistPlaying = false
    print("Playlist has been wiped clean.")
end

-- GUI Creation - all elements are created and parented explicitly
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")

local MainMusicPlayerGUI = Instance.new("ScreenGui")
MainMusicPlayerGUI.Name = "MusicPlayerGUI"
MainMusicPlayerGUI.Parent = PlayerGui

local MainPanel = Instance.new("Frame")
MainPanel.Size = UDim2.new(0, 200, 0, 300)
MainPanel.Position = UDim2.new(1, -210, 0, 10)
MainPanel.AnchorPoint = Vector2.new(1, 0)
MainPanel.BackgroundColor3 = Color3.fromRGB(50, 50, 50)
MainPanel.BorderColor3 = Color3.fromRGB(20, 20, 20)
MainPanel.BorderSizePixel = 2
MainPanel.Draggable = true
MainPanel.Parent = MainMusicPlayerGUI

local HeaderFrame = Instance.new("Frame")
HeaderFrame.Size = UDim2.new(1, 0, 0, 30)
HeaderFrame.Position = UDim2.new(0, 0, 0, 0)
HeaderFrame.BackgroundColor3 = Color3.fromRGB(50, 50, 50)
HeaderFrame.BorderColor3 = Color3.fromRGB(20, 20, 20)
HeaderFrame.BorderSizePixel = 1
HeaderFrame.Parent = MainPanel

local HeaderLabel = Instance.new("TextLabel")
HeaderLabel.Size = UDim2.new(1, -60, 1, 0)
HeaderLabel.Position = UDim2.new(0, 0, 0, 0)
HeaderLabel.Text = "Music Player"
HeaderLabel.Font = Enum.Font.SourceSansBold
HeaderLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
HeaderLabel.TextSize = 18
HeaderLabel.BackgroundTransparency = 1
HeaderLabel.Parent = HeaderFrame
HeaderLabel.ZIndex = 2

-- Minimize Button for Main Panel
local MinimizeButton = Instance.new("TextButton")
MinimizeButton.Size = UDim2.new(0, 30, 0, 30)
MinimizeButton.Position = UDim2.new(1, -60, 0, 0)
MinimizeButton.Text = "-"
MinimizeButton.Font = Enum.Font.SourceSansBold
MinimizeButton.TextColor3 = Color3.fromRGB(255, 255, 255)
MinimizeButton.TextSize = 20
MinimizeButton.BackgroundColor3 = Color3.fromRGB(80, 80, 80)
MinimizeButton.BorderColor3 = Color3.fromRGB(20, 20, 20)
MinimizeButton.BorderSizePixel = 1
MinimizeButton.Parent = HeaderFrame
MinimizeButton.ZIndex = 2
MinimizeButton.MouseButton1Click:Connect(function()
    local contentContainer = MainPanel:FindFirstChild("ContentContainer")
    if contentContainer then
        contentContainer.Visible = not contentContainer.Visible
        if not contentContainer.Visible then
            MainPanel.Size = UDim2.new(0, 200, 0, 30) -- Only header visible
            MinimizeButton.Text = "+"
        else
            MainPanel.Size = UDim2.new(0, 200, 0, 300) -- Restore full size
            MinimizeButton.Text = "-"
        end
    end
end)

-- Destroy Button for Main Panel
local DestroyButton = Instance.new("TextButton")
DestroyButton.Size = UDim2.new(0, 30, 0, 30)
DestroyButton.Position = UDim2.new(1, -30, 0, 0)
DestroyButton.Text = "X"
DestroyButton.Font = Enum.Font.SourceSansBold
DestroyButton.TextColor3 = Color3.fromRGB(255, 255, 255)
DestroyButton.TextSize = 20
DestroyButton.BackgroundColor3 = Color3.fromRGB(150, 50, 50)
DestroyButton.BorderColor3 = Color3.fromRGB(20, 20, 20)
DestroyButton.BorderSizePixel = 1
DestroyButton.Parent = HeaderFrame
DestroyButton.ZIndex = 2
DestroyButton.MouseButton1Click:Connect(function()
    MainMusicPlayerGUI:Destroy()
    if _G.currentSound then
        stopSound()
    end
    print("Music Player GUI closed.")
end)

local ContentContainer = Instance.new("Frame")
ContentContainer.Size = UDim2.new(1, 0, 1, -30)
ContentContainer.Position = UDim2.new(0, 0, 0, 30)
ContentContainer.BackgroundTransparency = 1
ContentContainer.Parent = MainPanel
ContentContainer.Name = "ContentContainer"

local ScrollingFrame = Instance.new("ScrollingFrame")
ScrollingFrame.Size = UDim2.new(1, -10, 1, -50)
ScrollingFrame.Position = UDim2.new(0, 5, 0, 25)
ScrollingFrame.AnchorPoint = Vector2.new(0, 0)
ScrollingFrame.BackgroundColor3 = Color3.fromRGB(60, 60, 60)
ScrollingFrame.BorderColor3 = Color3.fromRGB(20, 20, 20)
ScrollingFrame.BorderSizePixel = 1
ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
ScrollingFrame.VerticalScrollBarInset = Enum.ScrollBarInset.Always
ScrollingFrame.ScrollBarImageColor3 = Color3.fromRGB(100, 100, 100)
ScrollingFrame.ScrollBarThickness = 6
ScrollingFrame.Parent = ContentContainer
ScrollingFrame.Active = true

local ButtonsContainer = Instance.new("Frame")
ButtonsContainer.Size = UDim2.new(1, 0, 1, 0)
ButtonsContainer.BackgroundTransparency = 1
ButtonsContainer.Parent = ScrollingFrame

local ListLayout = Instance.new("UIListLayout")
ListLayout.FillDirection = Enum.FillDirection.Vertical
ListLayout.Padding = UDim.new(0, BUTTON_PADDING)
ListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
ListLayout.VerticalAlignment = Enum.VerticalAlignment.Top
ListLayout.Parent = ButtonsContainer

-- Function to update the scrolling frame's CanvasSize
local function updateCanvasSize(scrollingFrameRef, listLayoutRef)
    task.wait(0.05) -- Small delay to allow UIListLayout to update AbsoluteContentSize
    local totalContentHeight = listLayoutRef.AbsoluteContentSize.Y
    local minCanvasHeight = scrollingFrameRef.AbsoluteSize.Y
    if minCanvasHeight == 0 then minCanvasHeight = 1 end -- Prevent division by zero

    if totalContentHeight < minCanvasHeight then
        scrollingFrameRef.CanvasSize = UDim2.new(0, 0, 0, minCanvasHeight + 1) -- Add 1 to ensure scrollbar appears if content is exactly minCanvasHeight
    else
        scrollingFrameRef.CanvasSize = UDim2.new(0, 0, 0, totalContentHeight)
    end
end

-- Connect to ChildAdded/Removed and layout property changes to update CanvasSize for Main Player
ButtonsContainer.ChildAdded:Connect(function() updateCanvasSize(ScrollingFrame, ListLayout) end)
ButtonsContainer.ChildRemoved:Connect(function() updateCanvasSize(ScrollingFrame, ListLayout) end)
ListLayout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function() updateCanvasSize(ScrollingFrame, ListLayout) end)
ScrollingFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(function() updateCanvasSize(ScrollingFrame, ListLayout) end) -- Important for dynamic resizing

-- Helper function to create uniform buttons
local function createContentButton(text, clickFunction)
    local button = Instance.new("TextButton")
    button.Size = UDim2.new(1, -10, 0, BUTTON_HEIGHT)
    button.Text = text
    button.Font = Enum.Font.SourceSans
    button.TextColor3 = Color3.fromRGB(255, 255, 255)
    button.TextSize = 14
    button.BackgroundColor3 = Color3.fromRGB(80, 80, 80)
    button.BorderColor3 = Color3.fromRGB(40, 40, 40)
    button.BorderSizePixel = 1
    button.Parent = ButtonsContainer
    button.ZIndex = 2
    button.MouseButton1Click:Connect(clickFunction)
    return button
end

-- Input fields for Asset ID and optional Song Name
local AssetIdInput = Instance.new("TextBox")
AssetIdInput.Size = UDim2.new(1, -10, 0, TEXTBOX_HEIGHT)
AssetIdInput.PlaceholderText = "Enter Asset ID"
AssetIdInput.Text = ""
AssetIdInput.Font = Enum.Font.SourceSans
AssetIdInput.TextColor3 = Color3.fromRGB(255, 255, 255)
AssetIdInput.TextSize = 14
AssetIdInput.BackgroundColor3 = Color3.fromRGB(90, 90, 90)
AssetIdInput.BorderColor3 = Color3.fromRGB(50, 50, 50)
AssetIdInput.BorderSizePixel = 1
AssetIdInput.Parent = ButtonsContainer
AssetIdInput.ClearTextOnFocus = true
AssetIdInput.ZIndex = 2

local SongNameInput = Instance.new("TextBox")
SongNameInput.Size = UDim2.new(1, -10, 0, TEXTBOX_HEIGHT)
SongNameInput.PlaceholderText = "Optional: Song Name for Playlist"
SongNameInput.Text = ""
SongNameInput.Font = Enum.Font.SourceSans
SongNameInput.TextColor3 = Color3.fromRGB(255, 255, 255)
SongNameInput.TextSize = 14
SongNameInput.BackgroundColor3 = Color3.fromRGB(90, 90, 90)
SongNameInput.BorderColor3 = Color3.fromRGB(50, 50, 50)
SongNameInput.BorderSizePixel = 1
SongNameInput.Parent = ButtonsContainer
AssetIdInput.ClearTextOnFocus = true
SongNameInput.ZIndex = 2

createContentButton("Play Music (by ID)", function()
    local assetId = AssetIdInput.Text
    if assetId and assetId ~= "" then
        playSound("rbxassetid://" .. assetId, DEFAULT_VOLUME, false)
    else
        print("Please enter an Asset ID to play.")
    end
end)

createContentButton("Stop Music", stopSound)

createContentButton("Add Custom Song to Playlist", function()
    local assetId = AssetIdInput.Text
    local songName = SongNameInput.Text
    if assetId and assetId ~= "" then
        handlePlaylistAdd(assetId, songName ~= "" and songName or nil)
        AssetIdInput.Text = ""
        SongNameInput.Text = ""
    else
        print("Please enter an Asset ID to add to playlist.")
    end
end)

createContentButton("Play/Pause Playlist", function()
    if #_G.playlist > 0 then
        if _G.currentSound and _G.currentSound.IsPlaying and _G.playlistPlaying then
            _G.currentSound:Pause()
            _G.playlistPlaying = false
            print("Playlist paused.")
        elseif _G.currentSound and not _G.currentSound.IsPlaying and not _G.playlistPlaying then
            _G.currentSound:Play()
            _G.playlistPlaying = true
            print("Playlist resumed.")
        elseif not _G.currentSound and not _G.playlistPlaying then
            _G.currentPlaylistIndex = 1
            _G.playlistPlaying = true
            local nextSong = _G.playlist[_G.currentPlaylistIndex]
            if nextSong then
                print("Starting playlist playback with: " .. (nextSong.name or nextSong.id:gsub("rbxassetid://", "")))
                playSound(nextSong.id, DEFAULT_VOLUME, false)
            else
                print("Playlist is empty or not active. Start by playing a playlist or add songs.")
            end
        end
    else
        print("Playlist is empty. Add songs first.")
    end
end)

createContentButton("Next Song", function()
    if _G.playlistPlaying and #_G.playlist > 0 then
        _G.currentPlaylistIndex = _G.currentPlaylistIndex + 1
        if _G.currentPlaylistIndex > #_G.playlist then
            _G.currentPlaylistIndex = (_G.loopAllPlaylist and 1) or 0
            if _G.currentPlaylistIndex == 0 then
                _G.playlistPlaying = false
                stopSound()
                print("Playlist finished!")
                return
            end
        end
        local nextSong = _G.playlist[_G.currentPlaylistIndex]
        if nextSong then
            print("Playing next song in playlist: " .. (nextSong.name or nextSong.id:gsub("rbxassetid://", "")))
            playSound(nextSong.id, DEFAULT_VOLUME, false)
        else
            print("Playlist is empty or not active. Start by playing a playlist or add songs.")
        end
    else
        print("Playlist is empty or not active. Start by playing a playlist or add songs.")
    end
end)

createContentButton("Previous Song", function()
    if _G.playlistPlaying and #_G.playlist > 0 then
        _G.currentPlaylistIndex = _G.currentPlaylistIndex - 1
        if _G.currentPlaylistIndex < 1 then
            _G.currentPlaylistIndex = #_G.playlist -- Loop back to end if at start
            if _G.currentPlaylistIndex == 0 then -- Handle empty playlist edge case
                _G.playlistPlaying = false
                stopSound()
                print("Playlist is empty, cannot go to previous song.")
                return
            end
        end
        local prevSong = _G.playlist[_G.currentPlaylistIndex]
        if prevSong then
            print("Playing previous song in playlist: " .. (prevSong.name or prevSong.id:gsub("rbxassetid://", "")))
            playSound(prevSong.id, DEFAULT_VOLUME, false)
        else
            print("Playlist is empty or not active. Cannot go to previous song.")
        end
    else
        print("Playlist is empty or not active. Cannot go to previous song.")
    end
end)

createContentButton("Toggle Loop Current Song", function()
    _G.loopCurrentSong = not _G.loopCurrentSong
    print("Loop Current Song: " .. tostring(_G.loopCurrentSong))
    if _G.currentSound then
        _G.currentSound.Looped = _G.loopCurrentSong
    end
end)

createContentButton("Toggle Loop All Playlist", function()
    _G.loopAllPlaylist = not _G.loopAllPlaylist
    print("Loop All Playlist: " .. tostring(_G.loopAllPlaylist))
end)

createContentButton("Remove Song from Playlist", function()
    local query = AssetIdInput.Text
    local nameQuery = SongNameInput.Text
    if query ~= "" then
        handlePlaylistRemove(query)
    elseif nameQuery ~= "" then
        handlePlaylistRemove(nameQuery)
    else
        print("Please enter an Asset ID or Song Name in the top boxes to remove.")
    end
    AssetIdInput.Text = ""
    SongNameInput.Text = ""
end)

createContentButton("Wipe Entire Playlist", function()
    if AssetIdInput:IsFocused() or SongNameInput:IsFocused() then
        print("Please unfocus the text boxes first, then click 'Wipe Entire Playlist' again to confirm.")
    else
        wipePlaylist()
    end
end)

createContentButton("List Playlist", handlePlaylistList)

-- NEW: Volume Text Box and Set Volume Button
local VolumeInput = Instance.new("TextBox")
VolumeInput.Size = UDim2.new(1, -10, 0, TEXTBOX_HEIGHT)
VolumeInput.PlaceholderText = "Enter Volume (0-1, e.g., 0.5)"
VolumeInput.Text = tostring(DEFAULT_VOLUME)
VolumeInput.Font = Enum.Font.SourceSans
VolumeInput.TextColor3 = Color3.fromRGB(255, 255, 255)
VolumeInput.TextSize = 14
VolumeInput.BackgroundColor3 = Color3.fromRGB(90, 90, 90)
VolumeInput.BorderColor3 = Color3.fromRGB(50, 50, 50)
VolumeInput.BorderSizePixel = 1
VolumeInput.Parent = ButtonsContainer
VolumeInput.ClearTextOnFocus = true
VolumeInput.ZIndex = 2

createContentButton("Set Volume", function()
    local volumeText = VolumeInput.Text
    local newVolume = tonumber(volumeText)
    if newVolume and newVolume >= 0 and newVolume <= 1 then
        if _G.currentSound then
            _G.currentSound.Volume = newVolume
            print("Volume set to: " .. newVolume)
        else
            print("No sound currently playing to set volume for. Default volume is " .. DEFAULT_VOLUME .. ".")
        end
    else
        print("Invalid volume. Please enter a number between 0 and 1.")
    end
end)

--- Popular Songs UI ---
local PopularSongsGUI = Instance.new("ScreenGui")
PopularSongsGUI.Name = "PopularSongsGUI"
PopularSongsGUI.Parent = PlayerGui
PopularSongsGUI.Enabled = false -- Hidden by default

local PopularSongsPanel = Instance.new("Frame")
PopularSongsPanel.Size = UDim2.new(0, 400, 0, 450) -- Widened the panel
-- CHANGE THIS LINE:
PopularSongsPanel.Position = UDim2.new(0.5, -100, 0.4, 0) -- This moves the panel's TOP edge to 40% down the screen
-- If you want to move it further UP, decrease the '0.4' value.
-- For example, 0.35, 0.3, etc.
PopularSongsPanel.AnchorPoint = Vector2.new(0, 0) -- Top-left aligned for centering calculations
PopularSongsPanel.BackgroundColor3 = Color3.fromRGB(50, 50, 50)
PopularSongsPanel.BorderColor3 = Color3.fromRGB(20, 20, 20)
PopularSongsPanel.BorderSizePixel = 2
PopularSongsPanel.Draggable = true
PopularSongsPanel.Parent = PopularSongsGUI

local PSHeaderFrame = Instance.new("Frame")
PSHeaderFrame.Size = UDim2.new(1, 0, 0, 30)
PSHeaderFrame.Position = UDim2.new(0, 0, 0, 0)
PSHeaderFrame.BackgroundColor3 = Color3.fromRGB(50, 50, 50)
PSHeaderFrame.BorderColor3 = Color3.fromRGB(20, 20, 20)
PSHeaderFrame.BorderSizePixel = 1
PSHeaderFrame.Parent = PopularSongsPanel

local PSHeaderLabel = Instance.new("TextLabel")
PSHeaderLabel.Size = UDim2.new(1, -30, 1, 0)
PSHeaderLabel.Position = UDim2.new(0, 0, 0, 0)
PSHeaderLabel.Text = "Popular Songs"
PSHeaderLabel.Font = Enum.Font.SourceSansBold
PSHeaderLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
PSHeaderLabel.TextSize = 18
PSHeaderLabel.BackgroundTransparency = 1
PSHeaderLabel.Parent = PSHeaderFrame
PSHeaderLabel.ZIndex = 2

local PSCloseButton = Instance.new("TextButton")
PSCloseButton.Size = UDim2.new(0, 30, 0, 30)
PSCloseButton.Position = UDim2.new(1, -30, 0, 0)
PSCloseButton.Text = "X"
PSCloseButton.Font = Enum.Font.SourceSansBold
PSCloseButton.TextColor3 = Color3.fromRGB(255, 255, 255)
PSCloseButton.TextSize = 20
PSCloseButton.BackgroundColor3 = Color3.fromRGB(150, 50, 50)
PSCloseButton.BorderColor3 = Color3.fromRGB(20, 20, 20)
PSCloseButton.BorderSizePixel = 1
PSCloseButton.Parent = PSHeaderFrame
PSCloseButton.ZIndex = 2
PSCloseButton.MouseButton1Click:Connect(function()
    PopularSongsGUI.Enabled = false
end)

local PSSubHeaderLabel = Instance.new("TextLabel")
PSSubHeaderLabel.Size = UDim2.new(1, 0, 0, 20)
PSSubHeaderLabel.Position = UDim2.new(0, 0, 0, 30)
PSSubHeaderLabel.Text = "Due to Roblox API limitations, this list is static. Special thanks to @spatxz on Blue App for fixing a comma i didnt see"
PSSubHeaderLabel.Font = Enum.Font.SourceSans
PSSubHeaderLabel.TextColor3 = Color3.fromRGB(200, 200, 200)
PSSubHeaderLabel.TextSize = 12
PSSubHeaderLabel.BackgroundTransparency = 1
PSSubHeaderLabel.Parent = PopularSongsPanel
PSSubHeaderLabel.ZIndex = 2


local PSScollingFrame = Instance.new("ScrollingFrame")
PSScollingFrame.Size = UDim2.new(1, -10, 1, -60) -- Adjusted for header and sub-header
PSScollingFrame.Position = UDim2.new(0, 5, 0, 55)
PSScollingFrame.AnchorPoint = Vector2.new(0, 0)
PSScollingFrame.BackgroundColor3 = Color3.fromRGB(60, 60, 60)
PSScollingFrame.BorderColor3 = Color3.fromRGB(20, 20, 20)
PSScollingFrame.BorderSizePixel = 1
PSScollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
PSScollingFrame.VerticalScrollBarInset = Enum.ScrollBarInset.Always
PSScollingFrame.ScrollBarImageColor3 = Color3.fromRGB(100, 100, 100)
PSScollingFrame.ScrollBarThickness = 6
PSScollingFrame.Parent = PopularSongsPanel
PSScollingFrame.Active = true

local PSButtonsContainer = Instance.new("Frame")
PSButtonsContainer.Size = UDim2.new(1, 0, 1, 0)
PSButtonsContainer.BackgroundTransparency = 1
PSButtonsContainer.Parent = PSScollingFrame

local PSListLayout = Instance.new("UIListLayout")
PSListLayout.FillDirection = Enum.FillDirection.Vertical
PSListLayout.Padding = UDim.new(0, BUTTON_PADDING)
PSListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
PSListLayout.VerticalAlignment = Enum.VerticalAlignment.Top
PSListLayout.Parent = PSButtonsContainer

-- Connect to ChildAdded/Removed and layout property changes to update CanvasSize for Popular Songs UI
PSButtonsContainer.ChildAdded:Connect(function() updateCanvasSize(PSScollingFrame, PSListLayout) end)
PSButtonsContainer.ChildRemoved:Connect(function() updateCanvasSize(PSScollingFrame, PSListLayout) end)
PSListLayout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function() updateCanvasSize(PSScollingFrame, PSListLayout) end)
PSScollingFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(function() updateCanvasSize(PSScollingFrame, PSListLayout) end)

-- FIX: Re-position PopularSongsPanel higher on screen
-- PopularSongsPanel.Position = UDim2.new(0.5, -200, 0.5, -200) -- Original, center
PopularSongsPanel.Position = UDim2.new(0.5, -200, 0.2, 0) -- Adjusted to 20% down from the top

local function populatePopularSongsUI()
    -- Clear existing buttons
    for _, child in ipairs(PSButtonsContainer:GetChildren()) do
        if child:IsA("Frame") then -- Check for the song entry frames
            child:Destroy()
        end
    end

    print("--- Loading Popular Songs ---")
    _G.filteredPopularSongs = {}
    local availableSongs = {}

    for _, id in ipairs(POPULAR_SONGS_IDS) do
        local isAvailable, name, defaultName = getAssetInfo(id)
        if isAvailable then
            table.insert(availableSongs, {id = id, name = name})
        else
            warn("Skipping unavailable popular song ID: " .. id .. " (Name: " .. defaultName .. ")")
        end
        task.wait(0.01) -- Yield to prevent script timeout on large lists
    end

    table.sort(availableSongs, function(a, b)
        return a.name < b.name
    end)

    if #availableSongs == 0 then
        local noSongsLabel = Instance.new("TextLabel")
        noSongsLabel.Size = UDim2.new(1, -10, 0, 40)
        noSongsLabel.Text = "No popular songs are currently available."
        noSongsLabel.Font = Enum.Font.SourceSans
        noSongsLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
        noSongsLabel.TextSize = 14
        noSongsLabel.BackgroundTransparency = 1
        noSongsLabel.Parent = PSButtonsContainer
    else
        for i, song in ipairs(availableSongs) do
            local songEntryFrame = Instance.new("Frame")
            -- We now need enough height for the name (0.5) and the buttons (0.5) plus some padding
            songEntryFrame.Size = UDim2.new(1, -10, 0, BUTTON_HEIGHT * 2.5) -- Increased total height for song entry
            songEntryFrame.BackgroundTransparency = 1
            songEntryFrame.Parent = PSButtonsContainer

            local songNameLabel = Instance.new("TextLabel")
            songNameLabel.Size = UDim2.new(1, 0, 0.4, 0) -- Give name 40% of vertical space
            songNameLabel.Position = UDim2.new(0, 0, 0, 0)
            songNameLabel.Text = song.name
            songNameLabel.Font = Enum.Font.SourceSansBold
            songNameLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
            songNameLabel.TextSize = 14
            songNameLabel.TextXAlignment = Enum.TextXAlignment.Left
            songNameLabel.BackgroundTransparency = 1
            songNameLabel.Parent = songEntryFrame

            -- Container for buttons to properly use UIGridLayout below the name
            local buttonsContainerInner = Instance.new("Frame")
            buttonsContainerInner.Size = UDim2.new(1, 0, 0.6, 0) -- Take remaining 60% of vertical space
            buttonsContainerInner.Position = UDim2.new(0, 0, 0.4, 0) -- Position below the song name label
            buttonsContainerInner.BackgroundTransparency = 1
            buttonsContainerInner.Parent = songEntryFrame

            local buttonsLayout = Instance.new("UIGridLayout")
            buttonsLayout.CellSize = UDim2.new(0.32, 0, 0.9, 0) -- Adjusted CellSize for better fit (slightly less than 1/3 width each)
            buttonsLayout.CellPadding = UDim2.new(0, 2, 0, 0) -- Small horizontal padding
            buttonsLayout.FillDirection = Enum.FillDirection.Horizontal
            buttonsLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
            buttonsLayout.StartCorner = Enum.StartCorner.TopLeft
            buttonsLayout.SortOrder = Enum.SortOrder.LayoutOrder
            buttonsLayout.Parent = buttonsContainerInner -- Parent to the new inner container

            -- Buttons now fill their parent (buttonsContainerInner) and are laid out by UIGridLayout
            local copyIDButton = Instance.new("TextButton")
            copyIDButton.Size = UDim2.new(1, 0, 1, 0) -- Fill the cell size defined by UIGridLayout
            copyIDButton.LayoutOrder = 1
            copyIDButton.Text = "Copy ID"
            copyIDButton.Font = Enum.Font.SourceSans
            copyIDButton.TextColor3 = Color3.fromRGB(255, 255, 255)
            copyIDButton.TextSize = 12
            copyIDButton.BackgroundColor3 = Color3.fromRGB(70, 70, 70)
            copyIDButton.BorderColor3 = Color3.fromRGB(30, 30, 30)
            copyIDButton.BorderSizePixel = 1
            copyIDButton.Parent = buttonsContainerInner
            copyIDButton.MouseButton1Click:Connect(function()
                UserInputService:SetClipboard(song.id)
                print("Copied ID: " .. song.id)
            end)

            local playButton = Instance.new("TextButton")
            playButton.Size = UDim2.new(1, 0, 1, 0) -- Fill the cell size
            playButton.LayoutOrder = 2
            playButton.Text = "Play"
            playButton.Font = Enum.Font.SourceSans
            playButton.TextColor3 = Color3.fromRGB(255, 255, 255)
            playButton.TextSize = 12
            playButton.BackgroundColor3 = Color3.fromRGB(60, 120, 60)
            playButton.BorderColor3 = Color3.fromRGB(30, 60, 30)
            playButton.BorderSizePixel = 1
            playButton.Parent = buttonsContainerInner
            playButton.MouseButton1Click:Connect(function()
                playSound("rbxassetid://" .. song.id, DEFAULT_VOLUME, false)
            end)

            local addToPlaylistButton = Instance.new("TextButton")
            addToPlaylistButton.Size = UDim2.new(1, 0, 1, 0) -- Fill the cell size
            addToPlaylistButton.LayoutOrder = 3
            addToPlaylistButton.Text = "Add to Playlist"
            addToPlaylistButton.Font = Enum.Font.SourceSans
            addToPlaylistButton.TextColor3 = Color3.fromRGB(255, 255, 255)
            addToPlaylistButton.TextSize = 12
            addToPlaylistButton.BackgroundColor3 = Color3.fromRGB(60, 60, 120)
            addToPlaylistButton.BorderColor3 = Color3.fromRGB(30, 30, 60)
            addToPlaylistButton.BorderSizePixel = 1
            addToPlaylistButton.Parent = buttonsContainerInner
            addToPlaylistButton.MouseButton1Click:Connect(function()
                handlePlaylistAdd(song.id, song.name)
            end)
        end
    end
    updateCanvasSize(PSScollingFrame, PSListLayout) -- Update canvas size after populating
    print("--- Popular Songs Loaded ---")
end

-- NEW: Button to show the Popular Songs UI
createContentButton("Show Popular Songs", function()
    PopularSongsGUI.Enabled = not PopularSongsGUI.Enabled
    if PopularSongsGUI.Enabled then
        populatePopularSongsUI()
    end
end)


-- Initial welcome message for console
print("Music Player loaded! Drag the UI around your screen. Use the F9 console for playlist details.")

Features & Information

Its a simple and good CLIENT music player that can play any ids, and has a somewhat good popular songs list that well… Just guesses the ids and stops after 153 requests… Well, enjoy!
#Song player#Free#Keyless

Community Comments (7)

fiikju88fiikju88Jul 17, 2025

havent tried it but its probably good

pinkblownephiapinkblownephiaJul 17, 2025

Você poderia adicionar uma forma de poder pesquisar as músicas do catálogo direto pelo script?

MrCookieMrCookieJul 17, 2025

Hey @pinkblowbephia, i will try to! Thanks for the suggestion

MrCookieMrCookieJul 17, 2025

@pinkblownephia sadly this doesnt seem to be possible, sorry!

MrCookieMrCookieJul 19, 2025(edited)

Hello everyone, im currently finding a few more music ids, but most of them are sadly down. Stay tuned! Also, i‘ll try to fix following issues: You cant drag the UI on mobile, The UI disappears when using „reset character“ (profanity filter), leaving the song playing too. Happy listening everyone! Update: yeaahhh you apparently cant fix those easily, i‘ll focus on adding more songs. Sorry!

travistiger14travistiger14Jul 23, 2025

FE?

OnurXD_404OnurXD_404Nov 29, 2025

serversided?

More Scripts for Universal Scripts