A favicon.ico => favicon.ico +0 -0
A index.html => index.html +153 -0
@@ 0,0 1,153 @@
+<!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, these are the places to go.</p>
+ <ul>
+ <li><a href='https://forum.melonland.net/index.php?topic=2280.0'>The Melonland Forum Topic</a></li>
+ <li><a href='https://git.aleteoryx.me/cgit/lfm_embed/'>The Git Repository</a></li>
+ <li>The Author's...</li>
+ <ul>
+ <li><a href='https://aleteoryx.me'>Website</a></li>
+ <li><a href='https://mk.aleteoryx.me/@admin'>Fedi</a></li>
+ <li><a href='mailto:webmaster@aleteoryx.me'>Email</a></li>
+ </ul>
+ </ul>
+ </section>
+ </main>
+ <footer>🄯 Aleteoryx 2024. Free forever. If you don't trust me, <a href='https://git.aleteoryx.me/cgit/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>
+
A logo.png => logo.png +0 -0
A style.css => style.css +90 -0
@@ 0,0 1,90 @@
+@import url('https://fonts.googleapis.com/css2?family=Geologica&family=Montserrat+Subrayada:wght@700&display=swap');
+
+:root {
+ --text-color: hsl(99, 36%, 81%);
+ --bg-color: hsl(290, 100%, 8%);
+ --bg2-color: hsl(290, 100%, 4%);
+ --em-color: hsl(294, 42%, 65%);
+ --sub-color: hsl(294, 42%, 50%);
+ --link-color: hsl(324, 42%, 55%);
+
+ font-family: 'Geologica', sans-serif;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ margin: 0px;
+
+ background: repeating-linear-gradient(45deg, var(--bg-color), var(--bg-color) 18px, var(--bg2-color) 18px, var(--bg2-color) 21px);
+ background-attachment: fixed;
+ color: var(--text-color);
+
+ padding-top: 5mm;
+}
+
+a {
+ color: var(--link-color);
+}
+
+section, footer {
+ margin-bottom: 1cm;
+ padding: 5mm;
+ border-radius: 3.5mm;
+
+ background: #fff1;
+ backdrop-filter: blur(5px);
+
+ max-width: 20cm;
+}
+
+hgroup,
+h1, h2, h3, h4, h5, h6 {
+ font-family: 'Montserrat Subrayada', sans-serif;
+
+}
+
+h1, h2, h3, h4, h5, h6 {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ color: var(--em-color)
+}
+
+hgroup > p {
+ color: var(--sub-color)
+}
+
+section > pre {
+ background: var(--bg2-color);
+ padding: 2.5mm;
+ border-radius: 2.5mm;
+
+ width: auto;
+}
+
+footer {
+ color: var(--sub-color);
+ margin-bottom: 5mm;
+}
+footer > p { margin: 0px; }
+
+code {
+ background: var(--bg2-color);
+ padding: 1mm;
+ border-radius: 2mm;
+}
+
+p {
+ line-height: 1.5;
+}
+
+:is(h1,h2,h3,h4,h5,h6) > a {
+ color: inherit;
+ text-decoration: none;
+}
+
+:is(h1,h2,h3,h4,h5,h6) > a:hover {
+ color: inherit;
+}