├── app.css ├── app.js ├── index.html ├── overlay.html └── spotify.js /app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aidenwallis/nowplaying/d1b2172c8a96ce246e02d4ad7bbc766294a185f6/app.css -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var App = {}; 2 | var userId = window.location.pathname.split('?')[0].substring(1); 3 | // var userId = '5b2e3e66f37dc906fc29d608'; 4 | 5 | var container = document.getElementById('container'); 6 | var currentAlbumCover = document.getElementById('album-current'); 7 | var newAlbumCover = document.getElementById('album-new'); 8 | var artistsElement = document.getElementById('artists'); 9 | var songName = document.getElementById('name'); 10 | 11 | function timeoutPromise(dur) { 12 | return new Promise(function(resolve) { 13 | setTimeout(function() { 14 | resolve(); 15 | }, dur); 16 | }); 17 | } 18 | 19 | function makeSongName(item) { 20 | return `${item.album && item.album.artists ? item.album.artists.map(a => a.name).join(', ') : 'Various Artists'} - ${item.name}`; 21 | } 22 | 23 | App.currentSong = ''; 24 | App.currentCover = ''; 25 | App.user = null; 26 | App.loadedCovers = {}; 27 | App.waitingSocket = false; 28 | App.socketReady = false; 29 | App.open = false; 30 | App.firstAlbumLoad = true; 31 | App.scrollingSong = false; 32 | App.scrollingArtists = false; 33 | 34 | App.fetchUser = function() { 35 | return fetch('https://spotify.aidenwallis.co.uk/user/details/' + userId) 36 | .then(function(response) { 37 | if (response.status === 404) { 38 | window.location = '/'; 39 | return; 40 | } 41 | if (response.status !== 200) { 42 | return timeoutPromise(2000) 43 | .then(function() { 44 | return App.fetchUser(); 45 | }); 46 | } 47 | return response.json(); 48 | }) 49 | .then(function(data) { 50 | App.user = data; 51 | return data; 52 | }) 53 | .catch(function(error) { 54 | return timeoutPromise(2000) 55 | .then(function() { 56 | return App.fetchUser(); 57 | }); 58 | }); 59 | }; 60 | 61 | App.refreshToken = function() { 62 | return fetch('https://spotify.aidenwallis.co.uk/user/refresh/' + userId, { method: 'POST' }) 63 | .then(function(response) { 64 | if (response.status !== 200) { 65 | return timeoutPromise(2000) 66 | .then(function() { 67 | return App.refreshToken(); 68 | }); 69 | } 70 | return response.json(); 71 | }) 72 | .then(function(json) { 73 | if (!json.token) { 74 | return timeoutPromise(2000) 75 | .then(function() { 76 | return App.refreshToken(); 77 | }); 78 | } 79 | App.user.token = json.token; 80 | return App.checkSong(); 81 | }) 82 | .catch(function(error) { 83 | return timeoutPromise(2000) 84 | .then(function() { 85 | return App.refreshToken(); 86 | }) 87 | }) 88 | 89 | } 90 | 91 | App.checkSong = function() { 92 | if (App.user.clientPoll) { 93 | return fetch('https://api.spotify.com/v1/me/player/currently-playing', { 94 | headers: { 95 | Authorization: `Bearer ${App.user.token}`, 96 | } 97 | }) 98 | .then(function(response) { 99 | if (response.status === 401) { 100 | return App.refreshToken(); 101 | } 102 | if (response.status === 429) { 103 | // Ratelimited. Wait till we're un-ratelimited 104 | if (response.headers.has('Retry-After')) { 105 | const delay = parseInt(response.headers.get('Retry-After')); 106 | return timeoutPromise((delay) + (Math.floor(Math.random() * 6) + 1) * 1000) // Random padding. 107 | .then(function() { 108 | App.checkSong(); 109 | }); 110 | } 111 | } 112 | if (response.status === 204) { 113 | // No song playing. 114 | if (App.open) { 115 | App.close(); 116 | } 117 | return timeoutPromise(10000) 118 | .then(function() { 119 | App.checkSong(); 120 | }); 121 | } 122 | return response.json().then(function(json) { 123 | if (!json.item && !json.hasOwnProperty('is_playing')) { 124 | // Spotify API error. 125 | return timeoutPromise(10000) 126 | .then(function() { 127 | App.checkSong(); 128 | }); 129 | } 130 | if (!json.is_playing) { 131 | if (App.open) { 132 | App.close(); 133 | } 134 | } else { 135 | const albumImages = json.item.album.images.reduce(function(acc, cur) { 136 | acc[cur.height] = cur.url; 137 | return acc; 138 | }, {}); 139 | const data = { 140 | songName: makeSongName(json.item), 141 | artists: json.item.artists, 142 | title: json.item.name, 143 | albumCover: albumImages[Math.max(...Object.keys(albumImages))], 144 | }; 145 | if (App.open) { 146 | App.startUpdate(data); 147 | } else { 148 | App.openElement(); 149 | return timeoutPromise(1200) 150 | .then(function() { 151 | App.startUpdate(data); 152 | return timeoutPromise(10000); 153 | }).then(function() { 154 | App.checkSong(); 155 | }); 156 | } 157 | } 158 | return timeoutPromise(10000).then(function() { 159 | App.checkSong(); 160 | }); 161 | }); 162 | }) 163 | .catch(function(error) { 164 | console.error(error); 165 | return timeoutPromise(15000) 166 | .then(function() { 167 | App.checkSong(); 168 | }); 169 | }); 170 | } 171 | return fetch('https://spotify.aidenwallis.co.uk/u/' + userId + '?json=true&ts=' + Date.now()) 172 | .then(function(response) { 173 | return response.json(); 174 | }) 175 | .then(function(data) { 176 | setTimeout(function() { 177 | App.checkSong(); 178 | }, 10 * 1000); 179 | if (data.error) { 180 | if (App.open) { 181 | App.close(); 182 | } 183 | return; 184 | } 185 | if (!App.open) { 186 | App.openElement(); 187 | setTimeout(function() { 188 | App.startUpdate(data); 189 | }, 1200); 190 | return; 191 | } 192 | App.startUpdate(data); 193 | }) 194 | .catch(function(err) { 195 | console.error(err); 196 | return timeoutPromise(10 * 1000) 197 | .then(function() { 198 | App.checkSong(); 199 | }); 200 | }); 201 | }; 202 | 203 | App.close = function() { 204 | App.open = false; 205 | App.firstAlbumLoad = true; 206 | App.currentCover = ''; 207 | App.currentSong = ''; 208 | songName.classList.add('drop'); 209 | setTimeout(function() { 210 | artistsElement.classList.add('drop'); 211 | }, 350); 212 | setTimeout(function() { 213 | songName.innerHTML = ''; 214 | artistsElement.innerHTML = ''; 215 | songName.className = ''; 216 | artistsElement.className = ''; 217 | App.scrollingSong = false; 218 | container.classList.remove('active'); 219 | }, 800); 220 | setTimeout(function() { 221 | container.classList.remove('raise'); 222 | }, 1350); 223 | setTimeout(function() { 224 | currentAlbumCover.src = ''; 225 | currentAlbumCover.classList.remove('active'); 226 | newAlbumCover.src = ''; 227 | newAlbumCover.classList.remove('active'); 228 | }, 1800); 229 | }; 230 | 231 | App.startUpdate = function(data) { 232 | if (App.currentSong !== data.songName) { 233 | App.currentSong = data.songName; 234 | App.updateSongName(data.artists, data.title); 235 | } 236 | if (App.currentCover !== data.albumCover) { 237 | App.currentCover = data.albumCover; 238 | App.updateCover(data.albumCover); 239 | } 240 | }; 241 | 242 | App.openElement = function() { 243 | App.open = true; 244 | container.classList.add('raise'); 245 | setTimeout(function() { 246 | container.classList.add('active'); 247 | }, 550); 248 | } 249 | 250 | App.updateSongName = function(artists = [], name) { 251 | const maxWidth = container.offsetWidth - 80; // padding for other shit 252 | artistsElement.classList.remove('active'); 253 | setTimeout(function() { 254 | songName.classList.remove('active'); 255 | }, 200); 256 | setTimeout(function() { 257 | artistsElement.textContent = artists.map(function(artist) { 258 | return artist.name; 259 | }).join(', '); 260 | artistsElement.classList.add('active'); 261 | 262 | void artistsElement.offsetWidth; 263 | 264 | if (artistsElement.offsetWidth > maxWidth) { 265 | if (!App.scrollingArtists) { 266 | App.scrollingArtists = true; 267 | artistsElement.classList.add('scrolling'); 268 | } 269 | } else { 270 | if (App.scrollingArtists) { 271 | App.scrollingArtists = false; 272 | artistsElement.classList.remove('scrolling'); 273 | } 274 | } 275 | }, 550); 276 | setTimeout(function() { 277 | songName.textContent = name; 278 | 279 | void songName.offsetWidth; 280 | 281 | if (songName.offsetWidth > maxWidth) { 282 | if (!App.scrollingSong) { 283 | App.scrollingSong = true; 284 | songName.classList.add('scrolling'); 285 | } 286 | } else { 287 | if (App.scrollingSong) { 288 | App.scrollingSong = false; 289 | songName.classList.remove('scrolling'); 290 | } 291 | } 292 | 293 | songName.classList.add('active'); 294 | }, 750); 295 | }; 296 | 297 | App.updateCover = function(cover) { 298 | newAlbumCover.src = cover; 299 | newAlbumCover.onload = function() { 300 | newAlbumCover.className += ' active'; 301 | if (App.firstAlbumLoad) { 302 | currentAlbumCover.classList.add('active'); 303 | } 304 | setTimeout(function() { 305 | currentAlbumCover.src = cover; 306 | newAlbumCover.classList.remove('active'); 307 | newAlbumCover.src = ''; 308 | }, 450); 309 | }; 310 | }; 311 | 312 | App.transitionCover = function(cover) { 313 | 314 | }; 315 | 316 | function playerError(error) { 317 | console.error("Failed to initialize player", error); 318 | window.location = '/'; 319 | } 320 | 321 | function reloadPlayer(err) { 322 | console.error(err); 323 | } 324 | 325 | // 326 | 327 | // window.onSpotifyWebPlaybackSDKReady = function() { 328 | // if (App.waitingSocket && App.user.socketable) { 329 | // App.openSocket(); 330 | // } 331 | // App.socketReady = true; 332 | // }; 333 | 334 | App.start = function() { 335 | App.fetchUser().then(function() { 336 | App.checkSong(); 337 | }); 338 | }; 339 | 340 | App.start(); 341 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |Please enter the ID that you find from this site after login in the box below.
80 | 83 |Enjoy the tool? Please consider supporting me on Patreon.
84 |