· 2 years ago · Nov 08, 2022, 12:10 PM
1<!DOCTYPE html>
2<html>
3 <head>
4 <title>Last.FM Now Playing</title>
5 <meta charset="UTF-8">
6 </head>
7 <body>
8 <div class="info slideout">
9 <img class="logo" id="logo" src="https://www.xaymar.com/wp-content/uploads/2016/08/Avatar.png"><span class="artist" id="artist">Now</span><span class="separator"> – </span><span class="title" id="title">Playing</span>
10 </div>
11 <style>
12 body {
13 background: transparent;
14 color: black;
15 border: 0 transparent;
16 padding: 2pt;
17 margin: 0;
18 text-align: left;
19 overflow: hidden;
20 }
21
22 .info {
23 display: inline-block;
24 width: auto;
25 height: 32pt;
26
27 padding: 4pt 8pt;
28 margin: 0;
29 margin-top: 16pt;
30
31 font-size: 24pt;
32 font-weight: ;bold;
33 font-family: "Roboto", "Open Sans", sans-serif, serif;
34
35 color: white;
36 opacity: 0.0;
37
38 background: rgba(0, 0, 0, 0.75);
39 box-shadow: 0px 0px 6pt 1pt rgba(0, 0, 0, 1.0);
40
41 border: 1px solid black;
42 border-radius: 8px;
43 }
44
45 .info .logo {
46 width: 40pt;
47 height: 40pt;
48 margin: 0;
49 margin-top: -10pt;
50 margin-left: -6pt;
51 margin-right: 4pt;
52 padding: 1px;
53
54 background: rgba(255,255,255, 0.5);
55 box-shadow: 0px 0px 6pt -2pt rgba(255, 255, 255, 1.0);
56 border: 1px solid black;
57 border-radius: 4px;
58 }
59
60 .info .artist,
61 .info .separator,
62 .info .title {
63 vertical-align: top;
64 }
65
66 .slideout {
67 animation-name: slide_out;
68 animation-direction: normal;
69 animation-duration: 500ms;
70 animation-delay: 0s;
71 animation-fill-mode: both;
72 animation-iteration-count: 1;
73 animation-play-state: running;
74 animation-timing-function: ease-in;
75 }
76
77 .slidein {
78 animation-name: slide_in;
79 animation-direction: normal;
80 animation-duration: 500ms;
81 animation-delay: 0s;
82 animation-fill-mode: both;
83 animation-iteration-count: 1;
84 animation-play-state: running;
85 animation-timing-function: ease-out;
86 }
87
88 .flip {
89 animation-name: slide_in, flip;
90 animation-direction: normal;
91 animation-duration: 500ms;
92 animation-delay: 0s;
93 animation-fill-mode: both;
94 animation-iteration-count: 1;
95 animation-play-state: running;
96 animation-timing-function: linear;
97 }
98
99 @keyframes slide_out {
100 from {
101 margin-top: 16pt;
102 opacity: 1.0;
103 }
104 to {
105 margin-top: 100%;
106 opacity: 0.0;
107 }
108 }
109
110
111 @keyframes slide_in {
112 from {
113 margin-top: 100%;
114 opacity: 0.0;
115 }
116 to {
117 margin-top: 16pt;
118 opacity: 1.0;
119 }
120 }
121
122 @keyframes flip {
123 0% {
124 transform: rotate3d(0);
125 }
126 40% {
127 transform: rotate3d(1, 0, 0, 0.25turn);
128 }
129 60% {
130 transform: rotate3d(1, 0, 0, 0.25turn);
131 }
132 100% {
133 transform: rotate3d(0);
134 }
135 }
136 </style>
137 <script>
138 let api = {
139 key: "4568a6208da7dc6c123c7f1b89950782",
140 };
141 let params = new URLSearchParams(window.location.search);
142 let playing = false;
143 let imgcache = new Image();
144
145 function requestUpdate(api_key, user) {
146 let url = `https://ws.audioscrobbler.com/2.0/?api_key=${api_key}&method=user.getRecentTracks&user=${user}&extended=1&limit=1&format=json`;
147
148 let request = new Request(url, {
149 "method": "GET",
150 });
151
152 return fetch(request);
153 }
154
155 function slide_out() {
156 let el_info = document.querySelector(".info");
157 el_info.classList.add("slideout");
158 el_info.classList.remove("slidein");
159 }
160
161 function slide_in() {
162 let el_info = document.querySelector(".info");
163 el_info.classList.add("slidein");
164 el_info.classList.remove("slideout");
165 }
166
167 function flip(cb) {
168 let el_info = document.querySelector(".info");
169 el_info.classList.add("flip");
170 setTimeout(cb, 200);
171 setTimeout(function() {el_info.classList.remove("flip");}, 500);
172 }
173
174 function successHandler(value) {
175 value.json().then(data => {
176 //console.log(data);
177 let track = data.recenttracks.track[0];
178 let img = track.image[0]["#text"];
179 for (let imgdata of track.image) {
180 img = imgdata["#text"];
181 }
182 if (img == "") {
183 let art = track.artist;
184 img = art.image[0]["#text"];
185 for (let imgdata of art.image) {
186 img = imgdata["#text"];
187 }
188 }
189 if (img == "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png") {
190 // Seems to be the default "no image" logo.
191 img = "https://cdn.streamelements.com/uploads/7aff8871-bd64-4366-935b-1d96e5b418f4.png";
192 }
193 imgcache.src = img;
194
195 let el_info = document.querySelector(".info");
196 let el_logo = document.querySelector(".info .logo");
197 let el_artist = document.querySelector(".info .artist");
198 let el_title = document.querySelector(".info .title");
199
200 if (track["@attr"] == undefined) {
201 if (playing) {
202 // Stop Playing
203 console.log("Stopped playing track.");
204 playing = false;
205 slide_out();
206 }
207 } else if (playing == false) {
208 // Start Playing
209 console.log("Started playing track.");
210 playing = true;
211 imgcache.src = img;
212 el_logo.src = img;
213 el_artist.innerText = track.artist.name;
214 el_title.innerText = track.name;
215 slide_in();
216 } else {
217 // Track Change
218 let old_artist = el_artist.innerText;
219 let old_title = el_title.innerText;
220 if ((old_artist != track.artist.name) || (old_title != track.name)) {
221 console.log("Changed track.");
222 flip(function() {
223 el_logo.src = img;
224 el_artist.innerText = track.artist.name;
225 el_title.innerText = track.name;
226 });
227 }
228 }
229 }, reason => {
230 slide_out();
231 })
232 setTimeout(tick, 5000);
233 }
234
235 function failureHandler(reason) {
236 console.log("Last.FM Query failed:", reason);
237 slide_out();
238 setTimeout(tick, 10000);
239 }
240
241 function tick() {
242 let rq = requestUpdate(api.key, params.get("user"));
243 rq.then(successHandler, failureHandler);
244 rq.catch(failureHandler);
245 }
246
247 function zoom() {
248 let width = document.documentElement.clientWidth;
249 let height = document.documentElement.clientHeight;
250 let csize = document.documentElement.getBoundingClientRect();
251 let wscale = width / csize.width;
252 let hscale = height / csize.height;
253 document.body.style.zoom = hscale;
254 }
255
256 (function() {
257 zoom();
258 tick();
259 })();
260 </script>
261 </body>
262</html>