├── .gitignore ├── README.md ├── spotispy ├── preview.gif ├── spotispy.png ├── README.md └── spotispy.js ├── play-random ├── preview.gif ├── playRandom.jpg ├── README.md └── playRandom.mjs └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | summa.js 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShivvy/spicetify-extensions/HEAD/README.md -------------------------------------------------------------------------------- /spotispy/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShivvy/spicetify-extensions/HEAD/spotispy/preview.gif -------------------------------------------------------------------------------- /play-random/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShivvy/spicetify-extensions/HEAD/play-random/preview.gif -------------------------------------------------------------------------------- /spotispy/spotispy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShivvy/spicetify-extensions/HEAD/spotispy/spotispy.png -------------------------------------------------------------------------------- /play-random/playRandom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechShivvy/spicetify-extensions/HEAD/play-random/playRandom.jpg -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Spotispy", 4 | "description": "Keep tabs on your pals. Whatever they listen to, anybody you decide to stalk, will be put to your queue.", 5 | "main": "spotispy/spotispy.js", 6 | "readme": "spotispy/README.md", 7 | "preview": "spotispy/preview.gif", 8 | "authors": [ 9 | { "name": "TechShivvy", "url": "https://github.com/TechShivvy" }, 10 | { "name": "SunsetTechuila", "url": "https://github.com/SunsetTechuila" } 11 | ], 12 | "tags": ["stalk", "spy", "friends"] 13 | }, 14 | { 15 | "name": "Play a Random Song", 16 | "description": "Click and let the app play a random track for you.", 17 | "main": "play-random/playRandom.mjs", 18 | "readme": "play-random/README.md", 19 | "preview": "play-random/playRandom.jpg", 20 | "authors": [ 21 | { "name": "TechShivvy", "url": "https://github.com/TechShivvy" }, 22 | { "name": "Hitesh1090", "url": "https://github.com/Hitesh1090" } 23 | ], 24 | "tags": ["random", "song"] 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /play-random/README.md: -------------------------------------------------------------------------------- 1 | # Play a Random Song 2 | 3 | [Spicetify](https://github.com/khanhas/spicetify-cli) extension to surprise yourself with a random track (plays a random song). 4 | 5 | ## Motivation 6 | 7 | Hey there! Welcome to my second Spicetify gig – a seemingly pointless (again?) yet oddly fascinating extension. The idea struck me on New Year's Eve when I pondered what my first song of the next year would/should be. Not wanting to leave it to playlist shuffles or specific searches, I crafted this extension to play a completely random track. It turns out, exploring new, sometimes obscure, songs is way more fun than I thought. Join me in this musical adventure, and who knows, you might stumble upon some hidden gems too! 8 | 9 | ## Install 10 | 11 | Copy `playRandom.mjs` into your [Spicetify](https://github.com/khanhas/spicetify-cli) extensions directory: 12 | | **Platform** | **Path** | 13 | |----------------|--------------------------------------------------------------------------------------| 14 | | **Linux** | `~/.config/spicetify/Extensions` or `$XDG_CONFIG_HOME/.config/spicetify/Extensions/` | 15 | | **MacOS** | `~/spicetify_data/Extensions` or `$SPICETIFY_CONFIG/Extensions` | 16 | | **Windows** | `%appdata%\spicetify\Extensions\` | 17 | 18 | After putting the extension file into the correct folder, run the following command to install the extension or install through marketplace: 19 | 20 | ```sh 21 | spicetify config extensions playRandom.mjs 22 | spicetify apply 23 | ``` 24 | 25 | Note: Using the `config` command to add the extension will always append the file name to the existing extensions list. It does not replace the whole key's value. 26 | 27 | Or you can manually edit your `config-xpui.ini` file. Add your desired extension filenames in the extensions key, separated them by the | character. 28 | Example: 29 | 30 | ```ini 31 | [AdditionalOptions] 32 | ... 33 | extensions = autoSkipVideo.js|bookmark.js|fullAppDisplay.js|playRandom.mjs 34 | ``` 35 | 36 | Then run: 37 | 38 | ```sh 39 | spicetify apply 40 | ``` 41 | 42 | ## Usage 43 | 44 | - Simply install the extension and click the shuffle button/icon located on the top bar(mostly top-left). 45 | - After a brief moment, the app will play a random track, adding an element of surprise to your music experience. 46 | - Note: It will clear your current context queue, but any songs queued manually by you will remain. If all manually queued songs are played, similar songs to the randomly played track will line up and play if you have the "Autoplay" setting enabled. 47 | 48 | [![Preview](preview.gif)](https://raw.githubusercontent.com/TechShivvy/spicetify-extensions/main/play-random/preview.gif) 49 | 50 | ## Credits 51 | 52 | - [Delusoire](https://github.com/Delusoire) for the optimization help! 53 | 54 | ## More 55 | 56 | 🌟 Like it? Gimme some love! 57 | 58 | [![Github Stars badge](https://img.shields.io/github/stars/TechShivvy/spicetify-extensions?logo=github&style=social)](https://github.com/TechShivvy/spicetify-extensions/) 59 | If you find any bugs, please [create a new issue](https://github.com/TechShivvy/spicetify-extensions/issues/new/choose) on the GitHub repo. 60 | ![https://github.com/TechShivvy/spicetify-extensions/issues](https://img.shields.io/github/issues/TechShivvy/spicetify-extensions?logo=github) 61 | -------------------------------------------------------------------------------- /spotispy/README.md: -------------------------------------------------------------------------------- 1 | # Spotispy 2 | 3 | [Spicetify](https://github.com/khanhas/spicetify-cli) extension to keep tabs on your pals. Whatever they listen to, anybody you decide to stalk, will be put to your queue. 4 | 5 | ## Motivation 6 | 7 | You might think this extension is a bit meh, and honestly, I'm on the same page! But here's the thing: I kinda enjoy checking out what tunes my friends are vibing to. I used to manually add their jam to my Queue, but I thought, "Why not automate it for a smoother experience?". So, I went ahead and did just that. Now, why do I get a kick out of peeping their music choices? It's not about being nosy, really. It's more like a rad way to discover tons of new music and genres. Plus, this project was my first-ever Spicetify gig, and I was super stoked to share it with everyone! 8 | 9 | ## Install 10 | 11 | Copy `spotispy.js` into your [Spicetify](https://github.com/khanhas/spicetify-cli) extensions directory: 12 | | **Platform** | **Path** | 13 | |----------------|--------------------------------------------------------------------------------------| 14 | | **Linux** | `~/.config/spicetify/Extensions` or `$XDG_CONFIG_HOME/.config/spicetify/Extensions/` | 15 | | **MacOS** | `~/spicetify_data/Extensions` or `$SPICETIFY_CONFIG/Extensions` | 16 | | **Windows** | `%appdata%\spicetify\Extensions\` | 17 | 18 | After putting the extension file into the correct folder, run the following command to install the extension or install through marketplace: 19 | 20 | ```sh 21 | spicetify config extensions spotispy.js 22 | spicetify apply 23 | ``` 24 | 25 | Note: Using the `config` command to add the extension will always append the file name to the existing extensions list. It does not replace the whole key's value. 26 | 27 | Or you can manually edit your `config-xpui.ini` file. Add your desired extension filenames in the extensions key, separated them by the | character. 28 | Example: 29 | 30 | ```ini 31 | [AdditionalOptions] 32 | ... 33 | extensions = autoSkipVideo.js|bookmark.js|fullAppDisplay.js|spotispy.js 34 | ``` 35 | 36 | Then run: 37 | 38 | ```sh 39 | spicetify apply 40 | ``` 41 | 42 | ## Usage 43 | 44 | - Choose from the menu all of your friends who you wish to stalk. Your queue will now be updated anytime the chosen friends change songs. 45 | - If you see that a song has been added to your queue but none of your friends are playing it, the visual updating in the friends activity window may have been delayed. You can update it instantly by simply toggling the friends menu. 46 | - When you choose someone from the list, it won't add the music that they are now listening to right at that moment. 47 | 48 | [![Preview](preview.gif)](https://raw.githubusercontent.com/TechShivvy/spicetify-extensions/main/spotispy/preview.gif) 49 | 50 | ## Credits 51 | 52 | - [Grigory](https://github.com/SunsetTechuila) for the coding assistance! 53 | 54 | ## More 55 | 56 | 🌟 Like it? Gimme some love! 57 | 58 | [![Github Stars badge](https://img.shields.io/github/stars/TechShivvy/spicetify-extensions?logo=github&style=social)](https://github.com/TechShivvy/spicetify-extensions/) 59 | If you find any bugs, please [create a new issue](https://github.com/TechShivvy/spicetify-extensions/issues/new/choose) on the GitHub repo. 60 | ![https://github.com/TechShivvy/spicetify-extensions/issues](https://img.shields.io/github/issues/TechShivvy/spicetify-extensions?logo=github) 61 | -------------------------------------------------------------------------------- /spotispy/spotispy.js: -------------------------------------------------------------------------------- 1 | (async function() { 2 | while (!Spicetify.React || !Spicetify.ReactDOM) { 3 | await new Promise(resolve => setTimeout(resolve, 10)); 4 | } 5 | var spotispy = (() => { 6 | // src/app.tsx 7 | var _Recorder = class { 8 | static async processEvent(event, friendUri) { 9 | if (_Recorder.subscribedFriendUris.includes(friendUri)) { 10 | console.log(event, friendUri); 11 | await Spicetify.Platform.PlayerAPI.addToQueue([ 12 | { uri: event.track.uri, uid: null } 13 | ]); 14 | } 15 | } 16 | static async getFriends() { 17 | var _a; 18 | while (!((_a = Spicetify.CosmosAsync) == null ? void 0 : _a.get)) { 19 | await new Promise((resolve) => setTimeout(resolve, 300)); 20 | } 21 | const { friends } = await Spicetify.CosmosAsync.get( 22 | "https://spclient.wg.spotify.com/presence-view/v1/buddylist" 23 | ); 24 | return friends.reverse(); 25 | } 26 | static getUriType(uris) { 27 | const uriObj = Spicetify.URI.fromString(uris[0]); 28 | switch (uriObj.type) { 29 | case Spicetify.URI.Type.PROFILE: 30 | return true; 31 | default: 32 | return false; 33 | } 34 | } 35 | static async startTracking() { 36 | var _a, _b; 37 | while (!((_b = (_a = Spicetify.Platform) == null ? void 0 : _a.BuddyFeedAPI) == null ? void 0 : _b.subscribeToBuddyActivity)) { 38 | await new Promise((resolve) => setTimeout(resolve, 300)); 39 | } 40 | const friends = await _Recorder.getFriends(); 41 | const friendUris = friends.map((friend) => friend.user.uri.split(":")[2]); 42 | friendUris.forEach((friendUri) => { 43 | Spicetify.Platform.BuddyFeedAPI.subscribeToBuddyActivity( 44 | friendUri, 45 | (event) => { 46 | _Recorder.processEvent(event, friendUri); 47 | } 48 | ); 49 | }); 50 | } 51 | static toggleSubscription(friendUri) { 52 | console.log(this.subscribedFriendUris); 53 | if (_Recorder.subscribedFriendUris.includes(friendUri)) { 54 | const index = _Recorder.subscribedFriendUris.indexOf(friendUri); 55 | if (index !== -1) { 56 | _Recorder.subscribedFriendUris.splice(index, 1); 57 | console.log("removed"); 58 | } 59 | } else { 60 | _Recorder.subscribedFriendUris.push(friendUri); 61 | console.log("pushed"); 62 | } 63 | } 64 | static async addSettingsButton() { 65 | var _a, _b, _c; 66 | while (!((_a = Spicetify.Topbar) == null ? void 0 : _a.Button) || !((_b = Spicetify.React) == null ? void 0 : _b.createElement) || !((_c = Spicetify.PopupModal) == null ? void 0 : _c.display)) { 67 | await new Promise((resolve) => setTimeout(resolve, 100)); 68 | } 69 | const { createElement } = Spicetify.React; 70 | const createSettingsContent = async () => { 71 | const friends = await _Recorder.getFriends(); 72 | const friendElements = friends.map((friend) => { 73 | const uri = friend.user.uri.split(":")[2]; 74 | return createElement( 75 | "li", 76 | { key: uri }, 77 | createElement("input", { 78 | type: "checkbox", 79 | id: uri, 80 | onChange: () => { 81 | _Recorder.toggleSubscription(uri); 82 | updateSelectAllCheckboxState(); 83 | } 84 | }), 85 | friend.user.name 86 | ); 87 | }); 88 | const selectAllCheckbox = createElement("input", { 89 | type: "checkbox", 90 | id: "select-all", 91 | onChange: (event) => { 92 | const checkboxes = document.querySelectorAll( 93 | "input[type='checkbox']" 94 | ); 95 | _Recorder.toggleSubscription(event.target.id); 96 | checkboxes.forEach((checkbox) => { 97 | if (event.target.checked) { 98 | if (!checkbox.checked) { 99 | checkbox.checked = true; 100 | _Recorder.toggleSubscription(checkbox.id); 101 | } 102 | } else { 103 | if (checkbox.checked) { 104 | checkbox.checked = false; 105 | _Recorder.toggleSubscription(checkbox.id); 106 | } 107 | } 108 | }); 109 | } 110 | }); 111 | function updateSelectAllCheckboxState() { 112 | const checkboxes = document.querySelectorAll( 113 | "input[type='checkbox']" 114 | ); 115 | let allChecked = true; 116 | checkboxes.forEach((checkbox) => { 117 | if (checkbox.id !== "select-all" && !checkbox.checked) { 118 | allChecked = false; 119 | return; 120 | } 121 | }); 122 | checkboxes.forEach((checkbox) => { 123 | if (checkbox.id === "select-all") { 124 | checkbox.checked = allChecked; 125 | return; 126 | } 127 | }); 128 | } 129 | return createElement( 130 | "div", 131 | null, 132 | selectAllCheckbox, 133 | createElement( 134 | "label", 135 | { 136 | htmlFor: "select-all", 137 | style: { fontStyle: "italic", fontWeight: "bold" } 138 | }, 139 | "Select All" 140 | ), 141 | createElement("ul", null, friendElements) 142 | ); 143 | }; 144 | new Spicetify.Topbar.Button( 145 | "Friends Activity Recorder", 146 | `${Spicetify.SVGIcons.edit}`, 147 | async () => { 148 | const content = await createSettingsContent(); 149 | Spicetify.PopupModal.display({ 150 | title: "Spotispy", 151 | content, 152 | isLarge: true 153 | }); 154 | _Recorder.subscribedFriendUris.forEach((friendUri) => { 155 | const element = document.getElementById( 156 | friendUri 157 | ); 158 | if (element) { 159 | element.checked = true; 160 | } 161 | }); 162 | }, 163 | false 164 | ); 165 | } 166 | }; 167 | var Recorder = _Recorder; 168 | Recorder.subscribedFriendUris = []; 169 | async function main() { 170 | Recorder.startTracking(); 171 | Recorder.addSettingsButton(); 172 | } 173 | var app_default = main; 174 | 175 | // node_modules/spicetify-creator/dist/temp/index.jsx 176 | (async () => { 177 | await app_default(); 178 | })(); 179 | })(); 180 | 181 | })(); -------------------------------------------------------------------------------- /play-random/playRandom.mjs: -------------------------------------------------------------------------------- 1 | import sample from "https://esm.sh/lodash.sample"; 2 | import random from "https://esm.sh/lodash.random"; 3 | 4 | (async function () { 5 | while (!Spicetify.React || !Spicetify.ReactDOM) { 6 | await new Promise((resolve) => setTimeout(resolve, 10)); 7 | } 8 | 9 | var main = (() => { 10 | // src/app.tsx 11 | var _PlayRandom = class { 12 | static async fetchPlaylistsFromUser(userUri) { 13 | try { 14 | const userId = userUri.split(":")[2]; 15 | const initialResponse = await Spicetify.CosmosAsync.get( 16 | `https://api.spotify.com/v1/users/${userId}/playlists?limit=1` 17 | ); 18 | const totalPlaylists = initialResponse.total; 19 | 20 | if (totalPlaylists == 0) { 21 | return { error: "this mf dont have no playlists." }; 22 | } 23 | if (totalPlaylists <= 50) { 24 | return { playlists: initialResponse, from: "lesser" }; 25 | } else if (totalPlaylists > 50) { 26 | const randomOffset = random(totalPlaylists - 1); 27 | const url = `https://api.spotify.com/v1/users/${userId}/playlists?limit=1&offset=${randomOffset}`; 28 | const response = await Spicetify.CosmosAsync.get(url); 29 | return { playlists: response, from: "more" }; 30 | } 31 | } catch (error) { 32 | return { error: "Sad Bruh - Error fetching data : " + error.message }; 33 | } 34 | } 35 | 36 | static async fetchPlaylist(response) { 37 | if (response.error) { 38 | console.log(response.error); 39 | return "nope"; 40 | } else { 41 | const playlistUri = sample(response.playlists.items).uri; 42 | console.log("Random ahh Playlist URI:", playlistUri); 43 | return playlistUri; 44 | } 45 | } 46 | 47 | static async doTheThing(userUri) { 48 | Spicetify.showNotification( 49 | sample([ 50 | "Sit tight, my friend. The wheels of randomness are turning; your tune is in the making.", 51 | "Chill, mate! Randomness is doing its thing, and your jam is on the way.", 52 | "Hold on, buddy. The dice of randomness are rolling, and your song is in the works.", 53 | "Hey, hang in there! The randomness wheel is spinning, searching up a track for you.", 54 | "Hold on, pal! The chaos engine is at play; your jam is currently in the making.", 55 | ]) 56 | ); 57 | const playlists = await _PlayRandom.fetchPlaylistsFromUser(userUri); 58 | console.log(playlists); 59 | const randomPlaylistUri = await _PlayRandom.fetchPlaylist(playlists); 60 | if (randomPlaylistUri !== "nope") { 61 | const trackUris = await _PlayRandom.fetchTracksFromPlaylist( 62 | randomPlaylistUri 63 | ); 64 | console.log(trackUris); 65 | if (trackUris && trackUris.length > 0) { 66 | let randomTrackUri; 67 | while (true) { 68 | randomTrackUri = sample(trackUris); 69 | try { 70 | const trackInfo = await Spicetify.CosmosAsync.get( 71 | `https://api.spotify.com/v1/tracks/${ 72 | randomTrackUri.split(":")[2] 73 | }` 74 | ); 75 | if (trackInfo.preview_url) { 76 | console.log("preview_url: ", trackInfo.preview_url); 77 | break; 78 | } else { 79 | console.error("Chosen song not playable"); 80 | await new Promise((resolve) => setTimeout(resolve, 1000)); 81 | } 82 | } catch (error) { 83 | console.error("Error Fetching Track: ", error); 84 | await new Promise((resolve) => setTimeout(resolve, 1000)); 85 | } 86 | } 87 | console.log("Random ahh Track URI:", randomTrackUri); 88 | await Spicetify.Player.playUri(randomTrackUri); 89 | Spicetify.showNotification( 90 | "Play Random - " + 91 | sample([ 92 | "There you have it!", 93 | "Here it is!", 94 | "Presenting...", 95 | "And here you have it!", 96 | ]) 97 | ); 98 | } else { 99 | console.log("No tracks :(((("); 100 | Spicetify.showNotification("No Tracks in the chosen playlist"); 101 | } 102 | } else if (playlists?.error) { 103 | console.log(playlists.error); 104 | Spicetify.showNotification( 105 | playlists.error.startsWith("Sad Bruh") 106 | ? "Server Error" 107 | : "No playlists in selected account" 108 | ); 109 | } 110 | } 111 | 112 | // added more buttons 113 | static async addButton() { 114 | var _a, _b; 115 | while ( 116 | !((_a = Spicetify.Topbar) == null ? void 0 : _a.Button) || 117 | !((_b = Spicetify.PopupModal) == null ? void 0 : _b.display) || 118 | !Spicetify.CosmosAsync || 119 | !Spicetify.Playbar 120 | ) { 121 | await new Promise((resolve) => setTimeout(resolve, 100)); 122 | } 123 | 124 | let targetUserUri = "spotify:user:thesoundsofspotify"; 125 | let autoplayEnabled = false; 126 | let lastTrackUri = null; 127 | 128 | // Play Random button 129 | new Spicetify.Topbar.Button( 130 | "Play a Random Song", 131 | `${Spicetify.SVGIcons.shuffle}`, 132 | async () => { 133 | await _PlayRandom.doTheThing(targetUserUri); 134 | }, 135 | false 136 | ); 137 | 138 | // Toggle Autoplay button 139 | new Spicetify.Topbar.Button( 140 | "Toggle Autoplay", 141 | `${Spicetify.SVGIcons.play}`, 142 | () => { 143 | autoplayEnabled = !autoplayEnabled; 144 | Spicetify.showNotification( 145 | autoplayEnabled ? "Autoplay: ON" : "Autoplay: OFF" 146 | ); 147 | }, 148 | false 149 | ); 150 | 151 | // Set Profile button 152 | new Spicetify.Topbar.Button( 153 | "Set Playlist Profile", 154 | `${Spicetify.SVGIcons.edit}`, 155 | async () => { 156 | openPlaylistProfileModal(); 157 | }, 158 | false 159 | ); 160 | 161 | // Track end detection 162 | setInterval(async () => { 163 | if (!autoplayEnabled || !Spicetify?.Player?.getProgress || !Spicetify?.Player?.getDuration) return; 164 | 165 | const progress = Spicetify.Player.getProgress(); 166 | const duration = Spicetify.Player.getDuration(); 167 | const currentUri = Spicetify.Player.data?.item.uri; 168 | 169 | console.log("[AUTO] progress:", progress); 170 | console.log("[AUTO] duration:", duration); 171 | console.log("[AUTO] uri:", currentUri); 172 | console.log("[AUTO] lastTrackUri:", lastTrackUri); 173 | 174 | if (!progress || !duration || !currentUri) return; 175 | 176 | if (progress >= duration - 1000 && currentUri !== lastTrackUri) { 177 | console.log("[AUTO] Track finished. Triggering new random..."); 178 | Spicetify.Player.pause(); 179 | lastTrackUri = currentUri; 180 | await _PlayRandom.doTheThing(targetUserUri); 181 | } 182 | }, 2000); 183 | 184 | // Hotkeys with Alt instead of Ctrl 185 | document.addEventListener("keydown", async (e) => { 186 | if (e.altKey && e.key === "a") { // Alt + A: Toggle autoplay 187 | autoplayEnabled = !autoplayEnabled; 188 | Spicetify.showNotification( 189 | `Autoplay turned ${autoplayEnabled ? "ON" : "OFF"}` 190 | ); 191 | } 192 | 193 | if (e.altKey && e.key === "r") { // Alt + R: Play random now 194 | await _PlayRandom.doTheThing(targetUserUri); 195 | } 196 | 197 | if (e.altKey && e.key === "e") { // Alt + E: Open playlist URI modal 198 | openPlaylistProfileModal(); 199 | } 200 | }); 201 | 202 | // Modal function for setting playlist profile URI 203 | function openPlaylistProfileModal() { 204 | const modalContent = document.createElement("div"); 205 | modalContent.style.display = "flex"; 206 | modalContent.style.flexDirection = "column"; 207 | modalContent.style.gap = "10px"; 208 | 209 | const input = document.createElement("input"); 210 | input.type = "text"; 211 | input.placeholder = "spotify:user:your_username"; 212 | input.value = targetUserUri; 213 | input.style.padding = "0.375rem 0.75rem"; 214 | input.style.borderRadius = "0.25rem"; 215 | input.style.border = "1px solid #ced4da"; 216 | input.style.backgroundColor = "#fff"; 217 | input.style.color = "#212529"; 218 | input.style.width = "100%"; 219 | input.style.boxSizing = "border-box"; 220 | input.style.fontSize = "1rem"; 221 | input.style.transition = "border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out"; 222 | input.onfocus = () => { 223 | input.style.borderColor = "#80bdff"; 224 | input.style.outline = "0"; 225 | input.style.boxShadow = "0 0 0 0.2rem rgba(0,123,255,.25)"; 226 | }; 227 | input.onblur = () => { 228 | input.style.borderColor = "#ced4da"; 229 | input.style.boxShadow = "none"; 230 | }; 231 | 232 | const saveBtn = document.createElement("button"); 233 | saveBtn.textContent = "Save"; 234 | saveBtn.className = "main-button-button"; 235 | saveBtn.style.backgroundColor = "#1DB954"; 236 | saveBtn.style.color = "#fff"; 237 | saveBtn.style.border = "none"; 238 | saveBtn.style.padding = "0.375rem 0.75rem"; 239 | saveBtn.style.borderRadius = "0.25rem"; 240 | saveBtn.style.fontSize = "1rem"; 241 | saveBtn.style.cursor = "pointer"; 242 | saveBtn.style.transition = "background-color 0.15s ease-in-out"; 243 | saveBtn.onmouseenter = () => (saveBtn.style.backgroundColor = "#1e9548ff"); 244 | saveBtn.onmouseleave = () => (saveBtn.style.backgroundColor = "#1DB954"); 245 | saveBtn.onclick = () => { 246 | const val = input.value.trim(); 247 | if (/^spotify:user:[\w-]+$/.test(val)) { 248 | targetUserUri = val; 249 | Spicetify.showNotification("User URI updated!"); 250 | Spicetify.PopupModal.hide(); 251 | } else { 252 | Spicetify.showNotification("Invalid URI. Format: spotify:user:"); 253 | } 254 | }; 255 | 256 | const resetBtn = document.createElement("button"); 257 | resetBtn.textContent = "Reset to Default"; 258 | resetBtn.className = "main-button-button"; 259 | resetBtn.style.backgroundColor = "#6c757d"; 260 | resetBtn.style.color = "#fff"; 261 | resetBtn.style.border = "none"; 262 | resetBtn.style.padding = "0.375rem 0.75rem"; 263 | resetBtn.style.borderRadius = "0.25rem"; 264 | resetBtn.style.fontSize = "1rem"; 265 | resetBtn.style.cursor = "pointer"; 266 | resetBtn.style.transition = "background-color 0.15s ease-in-out"; 267 | resetBtn.onmouseenter = () => (resetBtn.style.backgroundColor = "#5c636a"); 268 | resetBtn.onmouseleave = () => (resetBtn.style.backgroundColor = "#6c757d"); 269 | resetBtn.onclick = () => { 270 | input.value = "spotify:user:thesoundsofspotify"; 271 | targetUserUri = "spotify:user:thesoundsofspotify"; 272 | Spicetify.showNotification("Reset to default profile."); 273 | }; 274 | 275 | modalContent.appendChild(input); 276 | modalContent.appendChild(saveBtn); 277 | modalContent.appendChild(resetBtn); 278 | 279 | Spicetify.PopupModal.display({ 280 | title: "Set Playlist Profile URI", 281 | content: modalContent, 282 | }); 283 | } 284 | } 285 | }; 286 | 287 | var PlayRandom = _PlayRandom; 288 | 289 | PlayRandom.fetchTracksFromPlaylist = async (uri) => { 290 | const res = await Spicetify.CosmosAsync.get( 291 | `sp://core-playlist/v1/playlist/${uri}/rows`, 292 | { 293 | policy: { link: true }, 294 | } 295 | ); 296 | return res.rows.map((item) => item.link); 297 | }; 298 | 299 | async function main() { 300 | PlayRandom.addButton(); 301 | } 302 | 303 | var app_default = main; 304 | 305 | (async () => { 306 | await app_default(); 307 | })(); 308 | })(); 309 | })(); 310 | --------------------------------------------------------------------------------