<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>scrobble.observer</title>
<link href="/style.css" rel="stylesheet" />
<style>
#wizard {
display: flex;
flex-direction: row;
}
#wizard * {
box-sizing: border-box;
}
#wizard > div {
margin: 2px;
}
#wizard-container > *,
#wizard-outputs > * {
width: 100%;
}
#wizard-container {
flex-grow: 1;
}
</style>
<!-- Open Graph Begin -->
<meta property="og:type" value="website">
<meta property="og:title" value="scrobble.observer">
<meta property="og:image" value="https://scrobble.observer/favicon.ico">
<meta property="og:url" value="https://scrobble.observer">
<meta property="og:description"
value="A little embed for your personal site, with whatever you're scrobbling.">
<meta property="og:site_name" value="scrobble.observer">
<!-- Open Graph End -->
</head>
<body>
<hgroup>
<h1>Scrobble.Observer</h1>
<p>A little embed for your personal site, with whatever you're scrobbling.</p>
</hgroup>
<main>
<section id=blurb>
<img src='/logo.png' id=logo style='margin-left: calc(50% - 64px); box-shadow: white 0px 0px 16px; border-radius: 14px;'>
<p><a href='https://last.fm'>Last.fm</a> is a cool service! It keeps a live, usually public, log, of whatever music you're listening to!
I felt like that fit into the general set of status buttons and other widgets present in the
Web Revival movement, and yet last.fm was basically stuck on <code>https://www.last.fm</code>. I decided to write an embed myself.
If you'd like to know about the technical details, the code and lengthy documentation are available on my git server (link at the bottom).</p>
<p>This project is not affiliated with or endorsed by last.fm at all, I'm just doing it for fun :></p>
</section>
<section id=intro>
<h2>Introduction</h2><br>
<iframe style='background-color: black; width: 60%; height: 3.2cm; margin-left: 20%' src='/user/LAST.HQ?dark'></iframe>
<p>This is the embed. Currently, it's displaying the listening history of the last.fm staff. It will update once-a-minute,
every minute, with whatever they're listening to. This rate limit is to avoid pinging the last.fm API too much, and because
few songs are shorter than 1 minute. This embed also uses no Javascript, instead relying on other mechanisms to refresh.</p>
</section>
<section id=generator>
<h2>Generate your embed!</h2><br>
<div id=wizard>
<div id=wizard-outputs style='width: 60%;'>
<iframe id=wizard-iframe style='height: 3.2cm; width: 100%; box-sizing: border-box;' src='/user/LAST.HQ?dark'></iframe>
<textarea id=wizard-code readonly style='width: 100%;' rows=4><iframe style='height: 3.2cm;' src='/user/LAST.HQ?theme=plain&dark'></iframe></textarea>
</div>
<div id=wizard-container style='width: 40%'>
<input type=text id=wizard-username placeholder='Your last.fm username, e.g. LAST.HQ' style='width: 100%;'>
<select id=wizard-theme><option selected disabled value='plain'>-- Choose a theme --</option></select><hr><p id=wizard-theme-notes></p><span>Options</span>
<select id=wizard-theme-params-bool multiple><option selected value='dark'>Dark Mode</option></select>
<div id=wizard-theme-params-text></div>
</div>
</div>
</section>
<section id=contact>
<h2>Contact</h2>
<p>This project is currently run by just one person, but if you need help or want to contribute, check out <a href='https://amehut.dev/~aleteoryx/scrobble.observer'>the project page</a></p>
</section>
</main>
<footer>🄯 Aleteoryx 2024. Free forever. If you don't trust me, <a href='https://git.amehut.dev/~aleteoryx/lfm_embed/'>host it yourself</a>.</footer>
<script>
const THEMES = {
"plain": {
"name": "Plain (Default)",
"notes": "This theme works best with <code>width: 10cm;</code>, at minimum. You'll need to figure out what looks best and add it to the style attribute manually on your site.",
"style": 'height: 3.2cm;',
"bool": {"dark": "Dark Mode"}
}
};
console.log(THEMES);
for (const theme in THEMES) {
const option = document.createElement('option');
option.value = theme;
option.innerText = THEMES[theme].name;
document.getElementById('wizard-theme').appendChild(option);
}
window.iframetimeout = null;
function regen(throttle) {
const username = document.getElementById('wizard-username').value.replace(" ", "") || 'LAST.HQ';
const themename = document.getElementById('wizard-theme').value;
const themeparams = Array.from(document.getElementById('wizard-theme-params-bool').selectedOptions).map(x => x.value);
const url = 'https://scrobble.observer/user/'+username+'?theme='+themename+(themeparams.length ? '&' + themeparams.join('&') : '');
clearTimeout(window.iframetimeout);
if (throttle) {
window.iframetimeout = setTimeout(() => (document.getElementById('wizard-iframe').src = url), 500);
}
else {
document.getElementById('wizard-iframe').src = url;
}
document.getElementById('wizard-code').value = "<iframe style='"+THEMES[themename].style+"' src='"+url+"'></iframe>"
}
function themechange() {
const themename = document.getElementById('wizard-theme').value;
const themeparamelement = document.getElementById('wizard-theme-params-bool');
const themenoteselement = document.getElementById('wizard-theme-notes');
themenoteselement.innerHTML = THEMES[themename].notes;
themeparamelement.innerHTML = '';
for (const boolparam in THEMES[themename]['bool']) {
const option = document.createElement('option');
option.value = boolparam;
option.innerText = THEMES[themename]['bool'][boolparam];
themeparamelement.appendChild(option);
}
regen();
}
document.getElementById('wizard-username').oninput = () => regen(true);
document.getElementById('wizard-theme').onchange = themechange;
document.getElementById('wizard-theme-params-bool').onchange = regen;
</script>
</body>
</html>